Non c'è modo "Linqy" built-in (si potrebbe gruppo, ma sarebbe piuttosto inefficiente), ma questo non significa che non si può fare il proprio modo:
public static IEnumerable<T> TakeDistinctByKey<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> keyFunc,
int count)
{
if (keyFunc == null)
throw new ArgumentNullException("keyFunc");
if (count <= 0)
yield break;
int currentCount = 0;
TKey lastKey = default(TKey);
bool isFirst = true;
foreach (T item in source)
{
yield return item;
TKey key = keyFunc(item);
if (!isFirst && (key != lastKey))
currentCount++;
if (currentCount > count)
yield break;
isFirst = false;
lastKey = key;
}
}
Poi è possibile richiamare con questo:
var items = cache.TakeDistinctByKey(rec => rec.Id, 20);
Se si dispone di chiavi composte o qualcosa di simile si potrebbe facilmente estendere il metodo di cui sopra per prendere un IEqualityComparer<TKey>
come argomento.
Si noti inoltre che ciò dipende dal fatto che gli elementi si trovano nell'ordine ordinato per chiave.Se non lo sono, si potrebbe o cambia l'algoritmo di cui sopra per usare un HashSet<TKey>
invece di un confronto conteggio e ultimo elemento dritto, o invocare con questo, invece:
var items = cache.OrderBy(rec => rec.Id).TakeDistinctByKey(rec => rec.Id, 20);
Edit - Mi piacerebbe anche Fai notare che in SQL avrei usato una query ROW_NUMBER
o una CTE ricorsiva, a seconda dei requisiti di prestazione: un + join è diverso da il metodo più efficiente. Se la cache è in ordine ordinato (o se è possibile modificarla in ordine ordinato), il metodo sopra riportato sarà di gran lunga il più economico in termini di tempo di memoria e di esecuzione.
Questo è fantastico. Lavori. LINQy. Probabilmente non sarà più veloce dell'originale, potrebbe essere più lento a seconda di cosa succede con GroupBy. – David
Sarà sempre un po 'più lento dell'originale perché deve eseguire un passaggio completo la prima volta; l'originale può fermarsi quando colpisce * n * elementi. Probabilmente non è un grosso problema se la lista è piccola. – Aaronaught
@Aaronaught: ma l'originale deve eseguire un passaggio completo nella seconda query, * e * eseguire una ricerca 'Contains' ad ogni passaggio. Quello potrebbe potenzialmente essere un vero killer delle prestazioni. Ovviamente, l'unico modo per sapere con certezza è di fare un benchmark con i dati del mondo reale. – LukeH