如何在LINQ查询中返回项的相邻项

时间:2011-02-23 14:49:42

标签: c# linq

考虑以下集合

var players = new[]{
     new {Id = 1, Name = "A", Score = 70},
     new {Id = 2, Name = "B", Score = 50},
     new {Id = 3, Name = "C", Score = 100},
     new {Id = 4, Name = "D", Score = 90}
 };

如果我想在得分排序的上面列表中返回特定玩家的位置(例如,ID = 1的玩家),我可以写一个这样的查询:

var result = players.OrderByDescending(p => p.Score)
             .Select((p, i) => new {player = p, Position = i})
             .Where(x => x.player.Id == 1)
             .First();
int position = result.Position;
var player = result.player;

现在除了实际播放器之外,我怎么能更进一步并返回相邻项目?当我们按分数订购列表时,相邻项目是上一个和下一个玩家及其各自的位置。

以下是查询的预期结果

var expectedResult = new[]{
    new {Id = 2, Name = "B", Score = 50},   //Previous player
    new {Id = 1, Name = "A", Score = 70},
    new {Id = 4, Name = "D", Score = 90}    //Next Player 
};

上述结果可以通过单个LINQ表达式实现吗? 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:4)

您可以使用.NET 4.0中定义的Zip运算符,或使用Rx扩展中定义的Scan运算符:

使用zip:

var result = players.OrderByDescending(p => p.Score)
    .Select((p, i) => new {Player = p, Position = i})
    .ToList(); //forces evaluation

result.Zip(result.Skip(1), (i,j) => new {First= i, Second=j})
      .Zip(result.Skip(2), (i,j) => new {First = i.First, Second = i.Second, Third=j})
      .First(o => o.Second.player.Id == 1);

但这不会给你第一个和最后一个球员的邻居。如果你也想要它们,你必须按摩你的收藏品(因为所有三个必须具有相同数量的物品)

答案 1 :(得分:1)

我写的是这样的:

public static IEnumerable<IList<T>> GetOverlappingChunks<T>(
    this IEnumerable<T> sequence, int chunkSize)
{
    List<T> chunk = new List<T>(chunkSize);

    foreach (var elt in sequence)
    {
        chunk.Add(elt);

        if (chunk.Count > chunkSize)
            chunk.RemoveAt(0);

        if (chunk.Count == chunkSize)
            yield return chunk.ToArray();
    }
}

// ...

var result = players.OrderByDescending(p => p.Score)
             .GetOverlappingChunks(3)
             .Where(x => x[1].Id == 1);

(太糟糕的C#没有内置双端队列类型。)

如果你需要处理列表中少于三个玩家的情况,那么你需要稍微调整GetOverlappingChunks和支票。