2017-02-20 6 views
4

Ich habe einen Code erstellt, der im Grunde zwei Listen in C# vergleicht. Erste Liste enthält Eigenschaften wie folgt aus:C#/LINQ schnellste Möglichkeit, zwei Listen zu vergleichen und Wert zuzuweisen

  • ItemID
  • TotalViews

erste Liste Werte für TotalViews fehlt so dass ich sie aus der 2. Liste zuweisen, der diese Requisiten:

  • ItemID
  • HitCount // dies ist eine Eigenschaft für TotalViews, die zugewiesen werden muss

Der Code ist wie folgt:

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

Gibt es eine effizientere Art und Weise dies mit LINQ oder die Implementierung eines benutzerdefinierten Vergleich zu schreiben, die schneller auf größere Listen funktionieren würde, die manchmal 100000 Elemente in sich enthält?

+6

Bitte bemühen Sie sich in Zukunft mehr, Ihre Frage zu formatieren. Sie haben jetzt über 100 Fragen gestellt - das ist viel Zeit, um sich mit Markdown vertraut zu machen. Es gibt eine Entschuldigung für eine schlechte Formatierung, wie es in Ihrem Post vor der Korrektur der Fall war. –

+2

Es wäre auch sehr hilfreich, wenn Sie ein [mcve] bereitstellen würden. Es gibt verschiedene Wege, dies zu erreichen ... ein Lexikon wäre ein offensichtlicher Ausgangspunkt, aber wir wissen nicht, ob es zum Beispiel zwei Elemente in "HitCountItemIDS" mit derselben ID geben könnte. –

+0

HitCountItemIDS darf keine doppelten Einträge enthalten, alle sind eindeutig und entsprechen der ersten Liste. Und ja, ich entschuldige mich, ich werde mich in Zukunft mehr anstrengen =) – User987

Antwort

2

ist die Pseudo-Code:

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 
} 

Die Idee ist MergeSort Algorithmen anzuwenden. Was die Komplexität angeht, geben Sie O (n * log (n)) aus, um jede Liste zu sortieren, und dann geht O (n) durch sie hindurch. Die Summe ist O (n * log (n)) und es ist der schnellste Weg, den ich sehe.

+1

Es gibt In diesem Fall muss nicht sortiert werden, und die Sortierung fügt Zeit hinzu. Die Verwendung einer Linq GroupBy() sollte schneller als C# -Code ausgeführt werden. – jdweng

2

Code würde wie folgt aussehen. Nicht sicher, welcher Typ von HitCountItemID ist. Wenn es anonym ist dann machen nur ‚var 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

wäre dies schneller als Merge sort und andere Methoden, die Leute erwähnt haben? – User987

+1

@ User987 - Nein, aber sicherlich sauberer. –

+0

@DmytroBogatov spielt es eine Rolle, ob parsedMerchantData concurrentbag oder list ist? Gerade jetzt wie es ist, ist es Art von Concurrentbag ... Sollte ich eine schnellere Leistung bekommen, wenn ich es in die Liste geworfen hätte? – User987

2

Ich gehe davon aus Sie halten 2 Listen während des Programmlaufs/Daten zu sammeln, so dass Sie sie während des Einsetzens sortieren. Oder wenn sie in DB sind und es einen Index auf der ID gibt, könnte es auch funktionieren.

Wenn ja, sollten Sie in der Lage sein, nur einen Durchlauf durch jedes Array zu machen, was das Programm wirklich hoch optimieren würde (jetzt haben Sie ungefähr n^2 Komplexität abhängig von Werten), nachdem Sie sich geändert haben.

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

Der Code sollte oben auf die wie folgt aussehen, sollten Sie etwas mehr Kontrolle über hinzufügen, wenn es endet &, was mit den übrigen Werten passiert sollte (dies einmal stoppt entweder i oder j traf das Ende). Hier

4

Diese wie jdweng Antwort ist, aber etwas einfacher, und es wird eine Ausnahme für fehlende Element-IDs nicht werfen:

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

Dies sollte O (N + M) sein, wobei N die Größe von HitCountItemIDs ist und M ist die Größe von parsedMerchantData ... also sollten die Daten größer werden, sollte es langsamer wachsen als der Merge-Sort-Ansatz, und ist definitiv einfacher Code. (Es ist nicht erforderlich, die Artikel-ID für die Bestellung zu vergleichen, entweder - nur Gleichheit.)

+0

Wow, was für eine schöne Optimierung, die beste Antwort von allen, sehr einfach und doch viel schneller als mein Original! =) – User987

Verwandte Themen