考虑以下集合
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表达式实现吗? 任何帮助将不胜感激。
答案 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
和支票。