How to get top N list items with LinQ?

I have an ordered list of exam points, and I want to have the top item N of this list.
If students N (th) and N + 1 (th) have the same exam points, the list should have both of them.

For example, I have a list like this:

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

Top 3 must return john, mike, james, ashley
I tried Take (), but it only returns john, mike, james

English is not my main language, sorry if I can’t say it right
Thanks

+6
source share
5 answers

A single pass implementation is implemented here:

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

Usage example:

 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); } 
+10
source

What you probably want to do is

  • Get nth
  • get everything when> = nth

i.e.

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

(It is assumed that the list is ordered in descending order, as in the example asked in the question. It will also throw an error if there are <n elements in the input list)

+2
source

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

You can group the points, then use OrderByDescending + Take + SelectMany :

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

I created a sample case in 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(); 

I don’t know if this is enough enough.

0
source

Maybe so?

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

Source: https://habr.com/ru/post/976002/


All Articles