c# - OrderBy and Top in LINQ with good performance -
what way top 10 records large collection , use custom orderby? if use linq objects orderby method slow , takes lot of memory because creates entire new collection new order. new method signature below not re-order entire collection , fast:
public static ienumerable<tsource> orderbytop<tsource, tkey>( ienumerable<tsource> source, func<tsource, tkey> keyselector, icomparer<tkey> comparer, int topcount)
i tried write got complicated , thought there might easier way using aggregate or something. appreciated.
answer
thanks help. ended code below:
public static list<tsource> orderbytop<tsource, tkey>( ienumerable<tsource> source, func<tsource, tkey> keyselector, icomparer<tkey> comparer, int topcount) { var itemcomparer = keyselector.toicomparer(comparer); return source.aggregate( new list<tsource>(topcount), (list<tsource> list, tsource item) => list.sortedinsert(item, itemcomparer, topcount)); }
the list extension method sortedinsert follows:
public static list<t> sortedinsert<t>( list<t> list, t item, icomparer<t> comparer, int maxlength) { if (list.count == maxlength) if (comparer.compare(item, list[maxlength - 1]) >= 0) return list; else list.removeat(maxlength - 1); int insertindex = list.binarysearch(item, comparer); if (insertindex < 0) insertindex = ~insertindex; list.insert(insertindex, item); return list; }
for interested had keyselector extension method convert icomparer.
public static icomparer<tsource> toicomparer<tsource, tkey>( func<tsource, tkey> keyselector, icomparer<tkey> comparer) { return new keyselectortoicomparerconverter<tsource, tkey>( keyselector, comparer); } private class keyselectortoicomparerconverter<tsource, tkey> : icomparer<tsource> { private readonly icomparer<tkey> comparer; private readonly func<tsource, tkey> keyselector; public keyselectortoicomparerconverter( func<tsource, tkey> keyselector, icomparer<tkey> comparer) { this.comparer = comparer; this.keyselector = keyselector; } public int compare(tsource x, tsource y) { return comparer.compare(keyselector(x), keyselector(y)); } }
aggregate
place start with:
sortedlist<tkey, tsource> resultlist = new sortedlist<tkey, tsource>(); mybiglist.aggregate(resultlist, (aktlist,entry) => { aktlist.add(entry.key, entry); if (aktlist.count > 10) aktlist.removeat(10); return aktlist; });
if want different comparer, can specify 1 in constructor of sortedlist
.
edit mentioned nikie, sortedlist
cannot contain double values. can use standard list binarysearch
achieve same effect:
list<tsource> resultlist = new list<tsource>(); mybiglist.aggregate(resultlist, (aktlist, entry) => { int index = aktlist.binarysearch(entry); if (index < 0) index = ~index; if (index < 10) aktlist.insert(index, entry); if (aktlist.count > 10) aktlist.removeat(10); return aktlist; });
again custom comparer (together custom key selection) can used parameter binarysearch
.
Comments
Post a Comment