14

Hi I have a lookup type that stores strings and ints.

static Lookup<string, int> lookup;
lookup = (Lookup<string, int>)list.ToLookup(i => i.IP, i => i.Number);

But now I need to sort this lookup by the values (number), and get the top 10 keys with their values.

How is this possible?

sprocket12
  • 5,368
  • 18
  • 64
  • 133
  • 1
    I'm trying to understand this. Why are you casting `i.Number` to `string`? – James Oct 29 '11 at 23:26
  • Hi sorry it should have been int. Ill change it now, also Ill mark an answer when I go home and try it tonight. Many thanks all. – sprocket12 Nov 02 '11 at 15:34

4 Answers4

7

Unfortunately elements inside a Lookup cannot be reordered.

But the ToLookup() method has a nice property that elements in all the groupings have the same order as the elements in the original sequence.

This means that with some Linq gymnastics, you can achieve what you want by using GroupBy:

var l = (from l in list
         // group elements by key
         group l by l.IP into g
         // for each group order the elements and take top 10
         select new { g.Key, Items = g.OrderBy(g1 => g1.Number).Take(10)} into g2
         // flaten group into an enumerable using select many
         from g in g2.Items
         select g)
         // get the desired lookup containing the top 10 ordered elements for each key
        .ToLookup(g => g.IP, g => g.Number);
Pop Catalin
  • 61,751
  • 23
  • 87
  • 115
4

I'm not sure why you are casting a Lookup<string, int> to a Lookup<string, string>, but the general answer you want is:

var list = new List<Test>
    {
            new Test { IP = "A", Number = 1 }, new Test { IP = "A", Number = 3 }, new Test { IP = "A", Number = 4 },
            new Test { IP = "B", Number = 1 }, new Test { IP = "B", Number = 1 }, new Test { IP = "B", Number = 1 },
            new Test { IP = "C", Number = 1 },
            new Test { IP = "D", Number = 1 },
            new Test { IP = "E", Number = 1 }, new Test { IP = "E", Number = 1 }, new Test { IP = "E", Number = 1 }
    };

var values = list.ToLookup(s => s.IP, s => s.Number)
                 .OrderByDescending(s => s.Count())
                 .Take(10);
Ritch Melton
  • 11,498
  • 4
  • 41
  • 54
  • Warning with this code, just in case the "list" is a database set of entities, calling `ToLookup()` similar to `ToList()` does not return IQueryable so it will bring to memory all the query data. Bad performance for a table with millions of records. – Jaider Apr 20 '19 at 01:03
  • How would you get a lookup otherwise? – Ritch Melton Apr 21 '19 at 21:55
1

Go find a Priority Queue (you can find one at http://www.itu.dk/research/c5/). Iterate over your look up and insert an IComparable item created from each entry in the look up, into the priority queue. Select the top ten items from the priority queue. Or just sort them by the count as the key.

var lookup = list.ToLookup( l => l.IP, l => l.Number );
var topten = lookup.OrderByDescending( l => l.Count() )
                   .Take( 10 );

foreach (var item in topten)
{
    Console.WriteLine( "{0}: {1}", item.Key, item.Count() );
}

Note that sorting will have at best O(nlogn) performance while a good, heap-based priority queue will have O(logn) performance. If the collection isn't large, sorting is simpler given the built in support for it and not needing an intermediate class to support the priority queue implementation.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
0

Take a look at the Take() LINQ function you should be able to do something like Take(10) to just return 10 results. As for sorting, check out the OrderBy() function that accepts a lambda expression as a sorting mechanism. Combining them both should give you what you're after.

arb
  • 7,753
  • 7
  • 31
  • 66