2016-11-24 23 views
5

Pracuję z matrycą wypełnioną podobieństwami między przedmiotami. Zapisuję je jako listę obiektów w mojej bazie danych. Obiekt Podobieństwo wygląda następująco:Warunkowe GroupBy() w LINQ

public class Similarity 
{ 
    public virtual Guid MatrixId { get; set; } //The id of the matrix the similarity is in 
    public virtual Guid FirstIndex { get; set; } //The id of the item of the left side of the matrix 
    public virtual Guid SecondIndex { get; set; } //The id of the item of the top side of the matrix 
    public virtual double Similarity { get; set; } //The similarity 
} 

Użytkownik może przeglądać te pozycje. Chcę pobrać listę pozycji, które są "podobne" do pozycji, które użytkownik sprawdził. Problem polega na tym, że nie jestem w stanie stwierdzić z całą pewnością, czy identyfikator elementu ma wartość FirstIndex lub SecondIndex. Napisałem kod, który robi to, co chcę, ale chcę wiedzieć, czy jest to możliwe w 1 instrukcji.

var itemsNotReviewed = Similarities.Where(x => !itemsReviewed.Contains(x.SecondIndex)) 
    .GroupBy(x => x.SecondIndex) 
    .ToList(); 
itemsNotReviewed.AddRange(Similarities.Where(x => !itemsReviewed.Contains(x.FirstIndex)) 
    .GroupBy(x => x.FirstIndex) 
    .ToList()); 

Gdzie itemsReviewed jest lista identyfikatorów GUID o elementach użytkownik dokonał przeglądu i gdzie Similarities znajduje się lista wszystkich przedmiotów, które są podobne do elementów użytkownik przeglądowi. Odzyskać tę listę z tej funkcji:

return (from Row in _context.SimilarityMatrix 
     where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex) 
     select Row) 
     .Distinct() 
     .ToList(); 

gdzie itemIds jest lista identyfikatorów GUID o elementach użytkownik przeglądowi.

Czy istnieje sposób grupowania według pierwszego lub drugiego indeksu na podstawie klauzuli Where?

Proszę dać mi znać, jeśli powinienem opracować!

+0

Nadzieja [połączyć Where i grupy przez w LINQ] (http: // stackoverflow.com/a/802337/3796048) może pomóc –

+0

@MohitShrivastava jak ustalić, który indeks jest kluczem do grupowania? – RandomStranger

Odpowiedz

3

Według mojego rozeznania, ci mieć listę Similarity, która z pewnością zawiera elementy o wartości FirstIndex lub SecondIndex zawarte w itemsReviewed liście Guid. I musisz wziąć elementy (jeśli istnieją) z indeksem nie zawarty w itemsReviewed (może to być tylko jeden z nich z powodu pierwszego ograniczenia) i grupy według tego indeksu.

Prosta tłumaczenie LINQ powyższe byłoby tak:

var itemsNotReviewed = Similarities 
    .Where(item => !itemsReviewed.Contains(item.FirstIndex) || !itemsReviewed.Contains(item.SecondIndex)) 
    .GroupBy(item => !itemsReviewed.Contains(item.FirstIndex) ? item.FirstIndex : item.SecondIndex) 
    .ToList(); 

Ale zawiera on powielać itemsReviewed.Contains kontrole, które wpływają negatywnie na wydajność.

Więc lepszym wariantem byłoby wprowadzenie zmiennej pośredniej i najprostszym sposobem na to jest składnia zapytań i let klauzula:

var itemsNotReviewed = 
    (from item in Similarities 
    let index = !itemsReviewed.Contains(item.FirstIndex) ? 1 : 
      !itemsReviewed.Contains(item.SecondIndex) ? 2 : 0 
    where index != 0 
    group item by index == 1 ? item.FirstIndex : item.SecondIndex) 
    .ToList(); 
+0

Doceniam wkład, ale dał mi rozwiązanie, które zapobiega problemowi, zamiast go rozwiązywać. Zrobiłem trochę myślenia na własną rękę i trochę go zmieniłem, żeby działało z moim kodem. – RandomStranger

+1

Nie ma problemu, jeśli tak jest - po tym wszystkim jest twój problem :) Mój post opiera się na ** Napisałem kod, który robi to, co chcę ** oświadczenie w twoim poście. Jeśli to nie pomoże, zaktualizuj pytanie, a ja usunę odpowiedź. Jestem pewien, że napisany przeze mnie kod robi dokładnie to, co robi twój kod, ale w jednym kroku. –

+1

Właśnie zdałem sobie sprawę, że masz rację, dałeś mi prawidłową odpowiedź na podstawie mojego posta, więc zasługujesz na nagrodę. Wymawianie się! – RandomStranger

1

pójdę na zmianę sposobu zaopatrywania oryginalną listę:

_context.SimilarityMatrix.Where(Row => itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex)) 
    .Select(r => new { r.MatrixId, r.FirstIndex, r.SecondIndex, r.Similarity, MatchingIndex = itemIds.Contains(r.FirstIndex) ? r.FirstIndex : r.SecondIndex }) 
    .Distinct() 
    .ToList(); 

ten sposób trzeba tylko grupy według Zestawy Index.

var itemsNotReviewed = Similarities. 
.GroupBy(x => x.MatchingIndex) 
.ToList(); 

Możesz przekonwertować obiekt dynamiczny na swoją klasę podobieństwa lub zmienić klasę tak, aby zawierała indeks dopasowania.

Można konwertować je do swojego typu podobieństwa przez:

var itemsNotReviewed = Similarities. 
.GroupBy(x => x.MatchingIndex) 
.Select(g => new { g.Key, Values = g.Values.Select(d => new Similarity { MatrixId = d.MatrixId, FirstIndex = d.FirstIndex, SecondIndex = d.SecondIndex, Similarity = d.Similarity }).ToList() }) 
.ToList(); 
+0

Więc 'MatchingIndex' będzie identyfikatorem z listy' itemIds'? – RandomStranger

+0

Dokładnie, będzie to identyfikator id elementu – Juan

+0

W porządku, ale pamiętając, że funkcja musiałaby zwrócić listę obiektów "podobieństwa", w jaki sposób przekonwertowałbym te anonimowe typy na obiekty? – RandomStranger

0

Co

(from x in Similarities 
let b2 = !itemsReviewed.Contains(x.SecondIndex) 
let b1 = !itemsReviewed.Contains(x.FirstIndex) 
where b1 || b2 
groupby b2 ? x.SecondIndex : x.FirstIndex into grp 
select grp) 
.ToList() 

Oświadczenie let wprowadza nowy tempoary zmienną przechowującą na wartość logiczną. Można oczywiście inline inną funkcję, za:

(from x in (from Row in _context.SimilarityMatrix 
      where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex) 
      select Row) 
      .Distinct() 
      .ToList() 
let b2 = !itemsReviewed.Contains(x.SecondIndex) 
let b1 = !itemsReviewed.Contains(x.FirstIndex) 
where b1 || b2 
groupby b2 ? x.SecondIndex : x.FirstIndex into group 
select group) 
.ToList() 

Jeśli chcesz użyć składni non-LINQ, prawdopodobnie trzeba wprowadzić pewne anonimowe typy:

Similarities 
.Select(s => new 
{ 
    b2 = !itemsReviewed.Contains(x.SecondIndex), 
    b1 = !itemsReviewed.Contains(x.FirstIndex), 
    s 
}) 
.Where(a => a.b1 || a.b2) 
.GroupBy(a => a.b2 ? a.s.SecondIndex : a.s.FirstIndex, a => a.x) //edit: to get same semantics, you of course also need the element selector 
.ToList() 
+0

Mam moje '_context' w repozytorium, więc nie mam dostępu do tego z mojej klasy. Również 'do grupy' powoduje ten błąd:' Niepoprawne wyrażenie "grupa". – RandomStranger

+0

Och, przepraszam, grupa to oczywiście używana nazwa, naprawi. – georch

+0

Jaki był twój drugi problem? Nie możesz uzyskać dostępu do _context z zewnątrz? – georch