2017-02-20 30 views
4

Я сделал код, который в основном сравнивает два списка в C#. Первый список содержит свойства, как это:C#/LINQ быстрый способ сравнения двух списков и присвоения значения

  • Itemid
  • TOTALVIEWS

Первый список испытывает недостаток значения для TOTALVIEWS поэтому я задаю их из 2-го списка, который имеет следующие реквизиты:

  • ItemID
  • HitCount // это свойство для TotalViews, которое должно быть назначено

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

foreach (var item in parsedMerchantData) 
{ 
    var itemInB = HitCountItemIDS.FirstOrDefault(x => x.ItemID == item.ItemID); 
    if (itemInB != null) 
    { 
     if (itemInB.HitCount != -1) 
     { 
      item.TotalViews = itemInB.HitCount; 
     } 
     else 
     { 
      item.TotalViews = 0; 
     } 
    } 
} 

Есть ли более эффективный способ, чтобы написать это с помощью LINQ или реализующего собственного компаратора, который будет работать быстрее на больших списках, содержит иногда 100000 элементов самого по себе?

+6

Просьба приложить больше усилий для форматирования вашего вопроса в будущем. Вы задали более 100 вопросов - это много времени, чтобы справиться с тем, как работает Markdown. Там есть повод для плохого форматирования, как ваш пост, прежде чем я его исправил. –

+2

Это также очень поможет, если вы предоставите [mcve]. Существуют различные способы приближения к этому ... словарь будет очевидной отправной точкой, но мы не знаем, могут ли быть два элемента в «HitCountItemIDS» с одним и тем же идентификатором, с одной стороны. –

+0

HitCountItemIDS не может содержать повторяющиеся элементы, все они уникальны, как и первый список. И да, мои извинения, я приложу в нее больше усилий в будущем =) – User987

ответ

2

Вот псевдо-код:

var arr1 = parsedMerchantData.OrderBy(x => x.ItemID).ToArray(); 
var arr2 = HitCountItemID.OrderBy(x => x.ItemID).ToArray(); 

var i, j = 0; 
while(i + j < arr1.Length() + arr2.Length()) // or similar condition 
{ 
    if (arr1[i].ItemID < arr2[j].ItemID) { 
     if (i < arr1.Length() - 1) { 
      i++; 
     } 
     continue; 
    } 

    if (arr1[i].ItemID > arr2[j].ItemID) { 
     if (j < arr2.Length() - 1) { 
      j++; 
     } 
     continue; 
    } 

    if (arr1[i].ItemID == arr2[j].ItemID) { 
     arr1[i].TotalViews = arr2[j].HitCount != -1 ? arr2[j].HitCount : 0; 
    } 

    // Make sure you do not let i and j grow higher then lengths of arrays 
} 

Идея заключается в том, чтобы применить алгоритмы сортировки слиянием. Что касается сложности, вы проводите O (n * log (n)), сортируя каждый список, тогда O (n) проходит через них. Сумма равна O (n * log (n)), и это самый быстрый способ, который я вижу.

+1

не нужно сортировать в этом случае, и сортировка добавляет Time. Использование Linq GroupBy() должно выполняться быстрее, чем код C#. – jdweng

2

Код будет выглядеть ниже. Не уверен, что такое тип HitCountItemID. Если анонимным, то просто сделать «уаг Dict»:

Dictionary<string, ABC_TYPE> dict = HitCountItemID.GropupBy(x => x.ItemID, y => y).ToDictionary(x => x.Key, y => y.FirstOrDefault()) 
foreach (var item in parsedMerchantData) 
{ 
    var itemInB = dict[item.ItemID]; 
    if (itemInB != null) 
    { 
     if (itemInB.HitCount != -1) 
     { 
      item.TotalViews = itemInB.HitCount; 
     } 
     else 
     { 
      item.TotalViews = 0; 
     } 
    } 
} 
+0

будет ли это быстрее, чем сортировка Merge и другие методы, о которых говорили люди? – User987

+1

@ User987 - Нет, но, безусловно, чище. –

+0

@DmytroBogatov имеет значение, если parsedMerchantData является параллельным или списком? Прямо сейчас, поскольку это тип параллельного пакета ... Должен ли я получить более высокую производительность, если бы я включил его в список? – User987

2

Я предполагаю, что Вы держите в руках 2 списка во время выполнения программы/сбора данных, таким образом Вы можете сортировать их во время установки. Или, если они находятся в БД, и есть индекс в ID, который он тоже работает.

Если это так, вы должны иметь возможность выполнять только один проход через каждый массив, что бы оптимизировать программу по-настоящему высоко (теперь вы получили о сложности n^2 в зависимости от значений), после того, как вы измените, у вас будет n.

int i = 0, j = 0; 

while(i < parsedMerchantData.Count && j < HitCountItemIDS.Count) 
{ 
    var item = parsedMerchantData[i]; 
    var itemInB = HitCountItemIDS[j]; 

    if (itemInB.ItemID == item.ItemID) 
    { 
     item.TotalViews = (itemInB.HitCount > 0) ? itemInB.HitCount : 0; 
     i++; 
     j++; 
    } 
    else if(itemInB.ItemID < item.ItemID) 
     i++; 
    else //itemInB.ItemID > item.ItemID 
     j++; 
} 

Код должен выглядеть так, как один выше, Вы должны добавить больше контроля над тем, когда она заканчивается &, что должно СЛУЧИЛОСЬ со значениями остальных (это остановит когда либо i или j попал в конец).

4

Это как ответ jdweng, но немного проще, и это не будет сгенерировано исключение для отсутствующих идентификаторы позиций:

var hitCountsById = HitCountItemIDS.ToDictionary(x => x.ItemID, x => x.HitCount); 
foreach (var item in parsedMerchantData) 
{ 
    int hitCount; 
    // We don't care about the return value of TryGetValue here... 
    hitCountsById.TryGetValue(item.ItemID, out hitCount); 
    item.HitCount = hitCount == -1 ? 0 : hitCount; 
} 

Это должно быть O (N + M), где N является размер HitCountItemIDs и M - размер parsedMerchantData ... так что, поскольку данные становятся больше, он должен расти медленнее, чем подход слияния-сортировки и, безусловно, более простой код. (Это не требует сравнения идентификатора товара для заказа - либо равенства.)

+0

Ничего себе, какая хорошая оптимизация, лучший ответ от всех, очень простой, но намного быстрее, чем мой оригинал! знак равно – User987