2015-11-04 4 views
5

Я хотел бы спросить, есть ли элегантный и эффективный способ объединить два списка MyClass в один?Объедините два списка в один, основанный на свойстве

MyClass выглядит следующим образом:

  • ID: int
  • Имя: string
  • ExtID: int?

и списки заполняются из различных источников и объектов в списках делятся ID, поэтому он выглядит так:

MyClass instance from List1 
ID = someInt 
Name = someString 
ExtID = null 

И MyCLASS экземпляр из List2

ID = someInt (same as List1) 
Name = someString (same as List1) 
ExtID = someInt 

То, что я в принципе нужно, чтобы объединить эти два списка, так что результат представляет собой список, содержащий:

ID = someInt (from List1) 
Name = someString (from List1) 
ExtID = someInt (null if no corresponding item - based on ID - on List2) 

Я знаю, что могу это сделать просто с помощью foreach loop, но мне бы хотелось узнать, есть ли более элегантный и, возможно, предпочтительный (из-за производительности, удобочитаемости) метод?

+0

напишите функцию «Слияние», которая объединяет два экземпляра с одним и тем же идентификатором «ID» в один, - тогда вы можете объединить списки, группу по идентификатору и, наконец, сбросить/скрыть по группам, используя вашу функцию слияния - вот как я это сделаю для чтения lity - для производительности вы, вероятно, отлично разбираетесь и цикл – Carsten

ответ

1

Существует множество подходов в зависимости от того, что является приоритетом, например. Союз + Поиск:

//this will create a key value pairs: id -> matching instances 
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID); 
//now just select for each ID the instance you want, ex. with some value 
var mergedInstances = idMap.Select(row => 
     row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First()); 

Выгода выше является то, что он будет работать с любым количеством независимо от списков, даже если они содержат много дублирующих isntances, а затем вы можете легко изменить условие слияния

Небольшого улучшения будет извлечь метод слияния экземпляров:

MyClass MergeInstances(IEnumerable<MyClass> instances){ 
    return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue) 
      ?? instances.First(); //or whatever else you imagine 
} 

и теперь просто использовать его в коде выше

var mergedInstances = idMap.Select(MergeInstances); 

Чистый, гибкий, простой, никаких дополнительных условий. Производительность мудрая не идеальна, но кому это нужно.

Edit: так как производительность является приоритетом, еще несколько вариантов

  1. Выполните поиск, как описано выше, но только для меньшего списка. Затем итерации по большему и выполните необходимые изменения O (m log m) + O (n). m - размер меньшего списка, n - больший размер списка - должен быть самым быстрым.

  2. Закажите оба списка по элементам ids. Создайте цикл for, который выполняет итерацию через оба из них, сохраняя текущий индекс для элемента с тем же идентификатором для обоих списков. Переместите указатель на следующий наименьший идентификатор, найденный в обоих списках, если он есть, переместите только это.O (n log n) + O (m log m) + O (n);

+0

Будет ли он работать быстрее, чем цикл foreach в List2 для соответствующих элементов в List1 и установить значение? ;> Если нет, то, поскольку он скрыт внутри частного метода библиотеки, я бы не прочь использовать foreach loop вместо этого, чтобы получить больше производительности, так как я не знаю, насколько большие списки могут расти и как часто это будет использоваться. Возможно, я сказал это неправильно, но приоритетом является производительность. – user1970395

+1

Если для каждого элемента вам нужно выполнить поиск по элементам из других списков, то вы получите сложность O (n^2), описанная выше будет быстрее, так как поиск равен O (log n) * O (n) Select которая является петлей foreach на самом деле O (n log n) vs O (n ^). Мои победы;). Использование Lookup table (словарь/карта) - это способ пойти в любом случае. И вы также получаете гибкость. Если вы можете улучшить существующее решение, вы можете подумать о том, чтобы предлагать словарь с самого начала, а не даже из списка. – mikus

+1

вы можете создать поиск (или словарь) в одном из списков, а затем просто выбрать второй, используя поиск, чтобы быстро найти значения. Также вы можете подумать о заказе обоих списков и сделать интеллектуальный цикл, который будет проходить один раз через оба списка O (n log n) + O (n). – mikus

-1

Я sugest создание цикла Еогеасп в методе этого класса, поэтому каждый раз, когда вам нужно сделать такую ​​вещь, вы бы использовать что-то вроде

instanceList1.MergeLists(instanceList2) 

и с помощью этого метода, вы можете контролируйте все, что вам нужно, при операции слияния.

+0

@mikus LINQ * делает * использовать итерацию и итераторы, поэтому комментарий о 'foreach' не применяется. С другой стороны, «написать свою собственную функцию» не является хорошим ответом, когда OP спрашивает, как записать эту функцию. Возможно, это должно быть удалено и повторно опубликовано в качестве комментария? –

+0

его о хорошем решении не решение, которое не будет использовать цикл внутри ... – mikus

+0

Ну, я просто предположил, что он знал, как выполнять эту функцию, и я, хотя это было бы лучше. (Он сказал, что он не хочет, чтобы предсказание да, но я, хотя он имел в виду, что он не хочет делать это каждый раз, когда ему нужно слить) –

1

Является ли это то, что вы хотите

var joined = from Item1 in list1 
     join Item2 in list2 
     on Item1.Id equals Item2.Id // join on some property 
     select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID); 

Edit: Если вы ищете для внешнего соединения,

var query = from Item1 in list1 
      join Item2 in list2 on Item1.Id equals Item2.Id into gj 
      from sublist2 in gj.DefaultIfEmpty() 
      select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty); 

читаемость мудры, используя цикл Еогеасп не слишком плохая идея ..

+0

, так как это внутреннее соединение выглядит так, будто оно не будет работать, если некоторые идентификаторы отсутствует в одном из списков – mikus

+0

да..другой..добавленный внешний случай соединения – Godsent

Смежные вопросы