关于c#:如何使用LinQ获取列表的前N个元素?

How to get top N elements of a list with LinQ?

我有一个按考试分数排序的列表,我想要这个列表的前n个元素。如果n(th)和n+1(th)学生的考试分数相同,则列表中必须同时包含这两个分数。

例如,我有一个这样的列表:

1
2
3
4
5
john.   80  
mike.   75  
james.  70  
ashley. 70
kate.   60

前三名应返回john, mike, james, ashley我试过take(),但它只返回john, mike, james

英语不是我的主要语言,对不起,如果我说得不正确谢谢


以下是一个仅通过一次的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static IEnumerable<TSource> TopWithTies<TSource, TValue>(
    this IEnumerable<TSource> source,
    int count,
    Func<TSource, TValue> selector)
{
    if (source == null) throw new ArgumentNullException("source");
    if (selector == null) throw new ArgumentNullException("selector");
    if (count < 0) throw new ArgumentOutOfRangeException("count");
    if (count == 0) yield break;
    using(var iter = source.OrderByDescending(selector).GetEnumerator())
    {
        if(iter.MoveNext())
        {
            yield return iter.Current;
            while (--count >= 0)
            {
                if(!iter.MoveNext()) yield break;
                yield return iter.Current;    
            }
            var lastVal = selector(iter.Current);
            var eq = EqualityComparer<TValue>.Default;
            while(iter.MoveNext() && eq.Equals(lastVal, selector(iter.Current)))
            {
                yield return iter.Current;
            }
        }
    }
}

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
var data = new[]
{
    new { name ="john", value = 80 },
    new { name ="mike", value = 75 },
    new { name ="james", value = 70 },
    new { name ="ashley", value = 70 },
    new { name ="kate", value = 60 }
};
var top = data.TopWithTies(3, x => x.value).ToList();
foreach(var row in top)
{
    Console.WriteLine("{0}: {1}", row.name, row.value);
}


你可能想做的是

  • 得到第n个
  • 当大于等于n时全部获取
  • 1
    2
    var nth = users.Skip(n-1).FirstOrDefault()
    var top = users.TakeWhile(user => user.Score >= nth.Score)

    (这假设列表按降序排列,如问题中给出的示例所示。如果输入列表中有


    也许是这样?

    1
    list.TakeWhile((item, index) => index < N || list[index] == list[index + 1]);


    我在LinqPad中创建了一个示例案例。

    1
    2
    3
    4
    5
    6
    7
    8
    var a = new List<Tuple<string,int>>();
    a.Add(new Tuple<string,int>("john",80));
    a.Add(new Tuple<string,int>("mike",75));
    a.Add(new Tuple<string,int>("james",70));
    a.Add(new Tuple<string,int>("ashley",70 ));
    a.Add(new Tuple<string,int>("kate",60  ));

    a.Where(x=>x.Item2>=a.OrderBy(i=>i.Item2).Skip(2).Take(1).SingleOrDefault ().Item2).Dump();

    但不知道它是否足够有效。


    What if more than two students have the same marks? Will you take them all? OP: Yes

    您可以按点分组,然后使用OrderByDescending+Take+SelectMany

    1
    2
    3
    4
    var topThreePoints = users.GroupBy(u => u.Points)
                              .OrderByDescending(g => g.Key)
                              .Take(3)
                              .SelectMany(g => g);