2017-02-23 13 views
6

Aktualnie pracuję nad aplikacją internetową w asp.net. W niektórych wywołaniach api należy porównać ListA z Listą B list, aby określić, czy ListA ma te same elementy dowolnej Listy na Liście B. Innymi słowy: jeśli lista ListA znajduje się na liście B.Jak skutecznie porównać listę?

Obie kolekcje są przeszukiwane za pomocą Linq kodu EF-Code-First db. ListB ma jedną pasującą listę lub żadną, nigdy więcej niż jedną. W najgorszym przypadku lista B zawiera miliony elementów, więc porównanie musi być skalowalne.

Zamiast robić zagnieżdżone pętle foreach, szukam czystego zapytania linq, które pozwoli db wykonać pracę. (Przed i rozważenia wielu indeks kolumny)

Aby zilustrować strukturę:

//In reality Lists are queried of EF 
var ListA = new List<Element>(); 
var ListB = new List<List<Element>>(); 
List<Element> solution; 
bool flag = false; 
foreach (List e1 in ListB) { 
    foreach(Element e2 in ListA) { 
     if (e1.Any(e => e.id == e2.id)) flag = true; 
     else { 
      flag = false; 
      break; 
     } 
    } 
     if(flag) { 
      solution = e1; 
      break; 
     } 
} 

Aktualizacja struktury

Od jego bazy danych EF będę zapewnić odpowiednie struktury obiektu. Nie jestem pewien, czy mogę umieszczać prawdziwy kod, więc ten przykład jest nadal ogólny.

//List B 
class Result { 
     ... 
     public int Id; 

     public virtual ICollection<Curve> curves; 

     ... 
} 

class Curve { 
     ... 
     public int Id; 

     public virtual Result result; 
     public int resultId; 

     public virtual ICollection<Point> points; 
     ... 
} 
public class Point{ 
    ... 
    public int Id; 
    ... 
} 

Sterownik (dla wywołania api) chce podawać właściwy obiekt zakrzywiony. Aby zidentyfikować właściwy obiekt, dostarczany jest filtr (ListA) (który jest w rzeczywistości obiektem typu Curve). Teraz należy porównać filtr (ListA) z Listą krzywych w wyniku (lista B). Jedyny sposób na porównanie Krzywe to porównanie obu Punktów. (Tak więc porównujące listy) Krzywe mają około 1 - 50 punktów. Wynik może mieć około 500.000.000 krzywych.

Możliwe jest porównanie według Tożsamości Obiektu, ponieważ wszystkie Obiekty (nawet filtr) są ponownie sprawdzane w db.

Szukam sposobu na wdrożenie tego mechanizmu, a nie jak obejść tę sytuację. (Np za pomocą wielofunkcyjnego indeks kolumny (zmieniając tabelę))

(dla celów poglądowych):

class controller { 
    ... 
    public Response serveRequest(Curve filter) { 
     foreach(Curve c in db.Result.curves) { 
       if(compare(filter.points , c.points)) return c; 

     } 
    } 
} 
+0

Twój kod się nie skompiluje, prosze o wpisanie prawdziwego kodu. obs: jest to 'var' – Lucas

+0

Musisz użyć sprzężenia wewnętrznego, ale bez znajomości struktury lepiej, trudno jej zasugerować. – Dexion

+0

Powiązane, ale nie dupe z powodu problemów EF tutaj: http://stackoverflow.com/questions/9524681/linq-compare-two-lists –

Odpowiedz

0

Spróbuj:

bool isIn = ListB.Any(x=>x.Count==ListA.Count && ListA.All(y=>x.Contains(y))); 

lub, jeśli chcesz elementowi

var solution = ListB.FirstOrDefault(x=>x.Count==ListA.Count && ListA.All(y=>x.Contains(y))); 
2

Użyj Except:

public static bool ContainsAllItems(IList<T> listA, IList<T> listB) 
    { 
     return !listB.Except(listA).Any(); 
    } 

Powyższa metoda pokaże, czy na liście A znajdują się wszystkie elementy listy B, czy też nie. Złożoność jest znacznie szybsza niż metoda O (n * m).

+0

Jeśli wskazuje na te same wystąpienia w pamięci, to zadziała, lub jeśli utworzy 'IEqualityComparer' – Lucas

+2

Ale jak to działa w SQL? –

0

mam coś dla Ciebie:

var db = new MyContext(); 

var a = db.LoadList(); // or whatever 
var b = new List<IQueryable<Entities>>(db.LoadListOfLists()/*or whatever*/); 

b.Any(x => x.Count.Equals(a.Count) & x.All(y => a.Any(z => z.Id == y.Id))); 
0

Ponieważ wydajność jest problemem, chciałbym zaproponować, aby przekształcić Lista Lookup/słownik przed porównaniem dawniej

var listALookup = listA.ToLookup(item => item.Id); 
var result = listB.FirstOrDefault(childList => childList.Count == listA.Count && childList.All(childListItem => listALookup.Contains(childListItem.Id))); 

Lookup.Contain wynosi O (1) while a List.Zawiera O (n)

Lepszą opcją jest wykonanie tego porównania na poziomie db, aby zmniejszyć ładowanie niepotrzebnych danych.