Существует нет встроенного способа «Linqy» (вы могли бы группу, но это было бы очень неэффективно), но это не значит, что вы не можете сделать свой собственный путь:
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;
}
}
Тогда вы можете вызвать его с этим:
var items = cache.TakeDistinctByKey(rec => rec.Id, 20);
Если у вас есть составные ключи или что-нибудь подобное, что вы могли бы легко распространить метод выше, чтобы принять IEqualityComparer<TKey>
в качестве аргумента.
Также обратите внимание, что это зависит от того, какие элементы находятся в отсортированном порядке по ключу.Если это не так, вы можете либо изменить алгоритм выше использовать HashSet<TKey>
вместо прямого счета и последний пункт сравнения, или вызвать его вместо этого:
var items = cache.OrderBy(rec => rec.Id).TakeDistinctByKey(rec => rec.Id, 20);
Edit - Я хотел бы также укажите, что в SQL я либо использовал бы запрос ROW_NUMBER
, либо рекурсивный CTE, в зависимости от требования к производительности - четкое + объединение - это , а не - самый эффективный метод. Если ваш кеш находится в отсортированном порядке (или если вы можете изменить его в отсортированном порядке), то указанный выше метод будет самым дешевым с точки зрения как памяти, так и времени выполнения.
Это замечательно. Работает. LINQy. Вероятно, не будет быстрее, чем ваш оригинал, может быть медленнее в зависимости от того, что происходит с GroupBy. – David
Он всегда будет немного медленнее оригинала, потому что он должен сделать полный проход в первый раз; оригинал может остановиться, когда он попадает * n * элементов. Вероятно, это не серьезная проблема, если список небольшой. – Aaronaught
@Aaronaught: Но оригинал должен выполнить полный проход во втором запросе, * и * выполнять поиск «Содержит» на каждом шаге. Это потенциально может быть настоящим убийцей производительности. Конечно, единственный способ узнать наверняка - это сравнить с реальными данными. – LukeH