Имеет ли LINQ
способ "запоминать" свои предыдущие результаты запроса при запросе?
Рассмотрим следующий случай:
public class Foo {
public int Id { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar {
public int Id { get; set; }
}
Теперь, если два или более Foo
имеют один и тот же набор Bar
(независимо от порядка), они считаются похожими Foo
.
Пример:
foo1.Bars = new List<Bar>() { bar1, bar2 };
foo2.Bars = new List<Bar>() { bar2, bar1 };
foo3.Bars = new List<Bar>() { bar3, bar1, bar2 };
В приведенном выше случае foo1
похож на foo2
, но оба foo1
и foo2
не похожи на foo3
Учитывая, что мы имеем результат query
, состоящий из IEnumerable
или IOrderedEnumerable
of Foo
. Из query
мы должны найти первый N
Foo
, который не является похожим.
Для этой задачи требуется память коллекции bars
, которая была выбрана ранее.
С частичным LINQ
мы могли бы сделать это следующим образом:
private bool areBarsSimilar(ICollection<Bar> bars1, ICollection<Bar> bars2) {
return bars1.Count == bars2.Count && //have the same amount of bars
!bars1.Select(x => x.Id)
.Except(bars2.Select(y => y.Id))
.Any(); //and when excepted does not return any element mean similar bar
}
public void somewhereWithQueryResult(){
.
.
List<Foo> topNFoos = new List<Foo>(); //this serves as a memory for the previous query
int N = 50; //can be any number
foreach (var q in query) { //query is IOrderedEnumerable or IEnumerable
if (topNFoos.Count == 0 || !topNFoos.Any(foo => areBarsSimilar(foo.Bars, q.Bars)))
topNFoos.Add(q);
if (topNFoos.Count >= N) //We have had enough Foo
break;
}
}
topNFoos
List
будет использоваться как память предыдущего запроса, и мы можем пропустить Foo q
в цикле foreach
, который уже имеет идентичный bars
с Any
Foo
в topNFoos
.
Мой вопрос: есть ли способ сделать это в LINQ
(полностью LINQ
)?
var topNFoos = from q in query
//put something
select q;
Если требуемая "память" относится к определенному элементу запроса q
или переменной вне запроса, мы могли бы использовать переменную let
для ее кеширования:
int index = 0;
var topNFoos = from q in query
let qc = index++ + q.Id //depends on q or variable outside like index, then it is OK
select q;
Но если это должно произойти из предыдущего запроса самого запроса, все начинает становиться более неприятным.
Есть ли способ сделать это?
Edit:
(Я в настоящее время создаю тестовый пример (github link) для ответов. Еще разобраться, как я могу проверить все ответы честно)
(Большинство ответов ниже направлены на решение моего конкретного вопроса и сами по себе хорошие (ответы Роба, Спайдера и Дэвида Б., которые используют IEqualityComparer
, особенно удивительны). Тем не менее, если есть кто-нибудь, кто может дать ответ на мой более общий вопрос: "LINQ имеет способ" запомнить "свои предыдущие результаты запроса при запросе", я также был бы рад)
(Помимо существенной разницы в производительности для конкретного случая, представленного выше при использовании полного/частичного LINQ, один ответ, направленный на то, чтобы ответить на мой общий вопрос о памяти LINQ, - это Иван Стоев, другой с хорошей комбинацией - Роб. сделайте себя яснее, я ищу общее и эффективное решение, если оно есть, используя LINQ)