简体   繁体   English

如何使用LinQ获取列表的前N个元素?

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

I have an ordered list by exam points and I want to have the top N element of this list. 我有一个按考试点排序的列表,我希望得到这个列表的前N个元素。
If the N(th) and N+1(th) students have the same exam points, the list must have them both. 如果N(th)和N + 1(th)学生具有相同的考试点,则列表必须同时具有它们。

For example I have a list like this: 例如,我有一个这样的列表:

john.   80  
mike.   75  
james.  70  
ashley. 70
kate.   60

Top 3 should return john, mike, james, ashley 前三名应该回到john, mike, james, ashley
I tried Take() but it returns only john, mike, james 我试过Take(),但它只返回john, mike, james

English is not my main language, sorry if I couldn't tell correctly 英语不是我的主要语言,对不起,如果我说不出来的话
Thanks 谢谢

Here's a one-pass-only implementation: 这是一个一次通过的实现:

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;
            }
        }
    }
}

Example usage: 用法示例:

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);
}

What you probably want to do is 你可能想做的是

  1. Get the nth 得到第n个
  2. get all when >= nth > = nth时全部获取

ie

var nth = users.Skip(n-1).FirstOrDefault()
var top = users.TakeWhile(user => user.Score >= nth.Score)

(This assumes that the list is ordered descending, as in the example given in the question. Also will throw an error if there are < n elements in the input list) (这假定列表按降序排序,如问题中给出的示例。如果输入列表中有<n个元素,也会抛出错误)

What if more than two students have the same marks? 如果两个以上的学生有相同的分数怎么办? Will you take them all? 你会把它们全部拿走吗? OP: Yes OP: 是的

You can group by the points, then use OrderByDescending + Take + SelectMany : 您可以按点进行分组,然后使用OrderByDescending + Take + SelectMany

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

I have created a sample case in LINQPad. 我在LINQPad中创建了一个示例案例。

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();

Don't know if it is efficient enough though. 不知道它是否足够有效。

也许是这样的?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM