2017-02-23 160 views
6

В настоящее время я работаю над веб-приложением в asp.net. В некоторых api-вызовах необходимо сравнивать ListA с ListB списков, чтобы определить, имеют ли ListA одинаковые элементы любого List в ListB. Другими словами: если ListA включен в ListB.Как эффективно сравнить список?

Обе коллекции запрашиваются у Linq EF-Code-First db. ListB имеет либо один соответствующий список или ни один, не более одного. В худшем случае ListB имеет миллионы элементов, поэтому сравнение должно быть масштабируемым.

Вместо того, чтобы делать вложенные петли foreach, я ищу чистый запрос linq, который позволит db выполнять работу. (Прежде чем я считать несколько индекс столбца)

Чтобы проиллюстрировать структуру:

//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; 
     } 
} 

Обновления структуру

Поскольку его база данных EF я буду предоставлять соответствующую структуру объекта. Я не уверен, что мне разрешено публиковать реальный код, поэтому этот пример по-прежнему является общим.

//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; 
    ... 
} 

Контроллер (для api-call) хочет обслуживать правый объект Curve-Object. Чтобы идентифицировать правильный объект, предоставляется фильтр (ListA) (который на самом деле является объектом кривой) Теперь фильтр (ListA) необходимо сравнить со списком кривых в результате (ListB) Единственный способ сравнения Кривые - это сравнение очков, которые имеют. (So infact сравнения списков) Кривые имеют около 1 - 50 очков. Результат может быть около 500.000.000 кривых

Здесь можно сравнить Object-Identity, потому что все объекты (даже фильтр) повторно запрашиваются из db.

Я ищу способ реализовать этот механизм, а не как обойти эту ситуацию. (Например, с помощью нескольких индекс столбца (изменяя таблицу))

(для иллюстрации):

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

     } 
    } 
} 
+0

Ваш код не скомпилируется, пожалуйста, внесите настоящий код. obs: это 'var' – Lucas

+0

Вам нужно использовать внутреннее соединение, но не зная структуру лучше, ее сложно предложить. – Dexion

+0

Связанный, но не обман из-за проблем EF здесь: http://stackoverflow.com/questions/9524681/linq-compare-two-lists –

ответ

0

Попробуйте это:

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

или, если вы хотите, элемент

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

Except:

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

Вышеуказанный метод подскажет, содержит ли listA все элементы listB или нет .. и сложность намного быстрее, чем O (n * m).

+0

Если он указывает на те же экземпляры в памяти, это будет работать, или если он создаст 'IEqualityComparer' – Lucas

+2

Но как это работает в SQL? –

0

У меня есть кое-что для вас:

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

Поскольку производительность озабоченность, я хотел бы предложить конвертировать lišta для поиска/словаря перед сравнением Экс-

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 является O (1), а List.Содержит O (n)

Лучшая опция - выполнить это сравнение на уровне db, чтобы уменьшить загрузку ненужных данных.