Vergleichen Sie Artikel in zwei Listen

86102
barry

Ich habe eine Methode, die zwei Listen von Strings vergleicht. Wenn die Zeichenfolgen und nur die Zeichenfolgen in listAerscheinen listB, kehren Sie zurück true. Sonst zurückkehren false.

internal bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds)
    {
        bool result = true;

        if (Ids.Count == XmlIds.Count)
        {
            foreach (var hwId in Ids)
            {
                if(!XmlIds.Contains(hwId))
                {
                    result = false;
                    break;
                }
            }
        }
        else
        {
            result = false;
        }

        return result;
    }

Mein anfänglicher Unit-Test ist bestanden (noch keine Randfälle usw.), aber ich habe mich gefragt, ob es einen kompakteren, aber dennoch lesbaren Weg gibt.

Antworten
13

6 Antworten auf die Frage

16
Jeff Mercado

Lassen Sie mich das klarstellen: Sie versuchen herauszufinden, ob Liste A eine Untermenge von Liste B ist?

internal bool DoIdsMatchThoseFromXml(List<string> ids, List<string> xmlIds)
{
    return !ids.Except(xmlIds).Any(); // A - B = {}
}

Oder versuchen Sie zu sehen, ob Liste A gleich Liste B ist?

internal bool DoIdsMatchThoseFromXml(List<string> ids, List<string> xmlIds)
{
    return ids.Count == xmlIds.Count // assumes unique values in each list
        && new HashSet<string>(ids).SetEquals(xmlIds);
}

Ich würde vermeiden, lineare Suchen durchzuführen, da Sie die Leistung von O (n * m) betrachten würden, wenn Sie O (n + m) ausführen könnten. Verwenden Sie Setoperationen, wenn Sie können. Verwenden Sie HashSet<string>wenn möglich auch.

Danke Jeff. Diese (LINQ?) Methoden sind neu für mich! Möchten Sie im zweiten Beispiel die `xmlIds.Count> 0` erläutern? Ich bin nicht sicher, was das hinzufügt. barry vor 8 Jahren 0
Im zweiten Fall müssen Sie sicherstellen, dass Satz B nicht leer ist. Andernfalls wird immer der Wert "true" (falsch) angegeben. Jeff Mercado vor 8 Jahren 0
Hmm ... mit dem nicht leeren Check ist es genau das gleiche wie im ersten Codebeispiel, außer dass die Listen die Plätze tauschen. Würde das zweite Beispiel also nicht zurückgeben, ob B eine Teilmenge von A war, so wie das erste Beispiel zurückgibt, ob A eine Teilmenge von B ist? barry vor 8 Jahren 0
So wie ich es sehe, wenn Sie prüfen, ob eine Menge A eine Teilmenge von Gruppe B ist, entfernen Sie einfach alle üblichen Elemente aus der Gruppe A, die ebenfalls in Gruppe B (`A - B`) enthalten ist, und es sollte die leere Menge ergeben. Die leere Menge (wie Gruppe A) ist eine Teilmenge aller Mengen, daher müssen wir dies berücksichtigen. Wenn Sie prüfen, ob die Gruppe A gleich der Gruppe B ist, müssen Sie, sofern die Gruppe B nicht leer ist, alle in Gruppe B enthaltenen Einheiten aus der Gruppe A entfernen, um die leere Gruppe zu erhalten. Es ist unwahrscheinlich, dass beide Listen leer sind. Daher habe ich diese Überprüfung ausgelassen (Sie können sie natürlich hinzufügen, wenn Sie möchten). Jeff Mercado vor 8 Jahren 0
In Beispiel 2 könnte Satz B jedoch nur eine Teilmenge von Satz A sein. Was ist, wenn A = {1,2,3,4} und B = {1,2}? Entschuldigung, wenn mir das Offensichtliche fehlt! barry vor 8 Jahren 0
Ah ja, du hast recht, in diesem Fall müsste es ein paar Modifikationen geben. Jeff Mercado vor 8 Jahren 0
6
Cristian Lupascu

Wenn Sie LINQ verwenden können, denke ich, können Sie dies tun

internal bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds)
{
    return 
        Ids.Count == XmlIds.Count &&
        Ids.All(XmlIds.Contains) &&
        XmlIds.All(Ids.Contains);
}

dasselbe Ergebnis erzielen.

Vielleicht klingt Ids.All (XmlIds.Contains) etwas Yoda-artig, aber ich denke, es ist immer noch lesbar. Cristian Lupascu vor 8 Jahren 2
:-) Yoda mag es ja klingen barry vor 8 Jahren 2
4
Jesse C. Slicer

Dies verwendet kein LINQ, sondern nur Methoden, die für Folgendes definiert sind List<T>:

    internal static bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds)
    {
        return Ids.TrueForAll(XmlIds.Contains) && XmlIds.TrueForAll(Ids.Contains);
    }
Ich mag, wie sauber dieser ist. Almo vor 5 Jahren 0
2
Mike Nakis

Die von Jeff und w0lf vorgeschlagenen Lösungen sind sehr gut, aber haben Sie in Betracht gezogen, Ihren Code einfach ein wenig umzuordnen? Denn obwohl O (M * N) Folgendes tut, war Ihre Frage nach Lesbarkeit, nicht nach Leistung, und ich denke, dass Leute, die mit linq nicht vertraut sind, dies für verständlicher halten werden:

internal bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds)
{
    if (Ids.Count != XmlIds.Count)
        return false;
    foreach (var hwId in Ids)
        if(!XmlIds.Contains(hwId))
            return false;
    return true;
}
Ich bin kein LINQ-Experte, aber ich fand sie hervorragend lesbar. Bei LINQ geht es darum, die Intention (was) über die Implementierung (wie) zu beschreiben. Gory Internals haben wir übergeordnete Sprachen und Bibliotheken erfunden. Jesse C. Slicer vor 8 Jahren 2
Ich muss zugeben, dass Sie recht haben. Also werde ich meine Antwort ein wenig umformulieren. Ich ändere "Menschen, die mit linq nicht so gut vertraut sind, werden dies lesbarer finden" in "Personen, die nicht mit linq vertraut sind, werden dies verständlicher finden". Mike Nakis vor 8 Jahren 0
2
Guffa

Dies ist kompakter, aber auch besser:

internal bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds) {
  if (Ids.Count != XmlIds.Count) return false;
  HashSet<string> xmlIds = new HashSet<string>(XmlIds);
  return Ids.All(id => xmlIds.Contains(id));
}

Dies ist nahe an O (n), verglichen mit dem ursprünglichen O (n * n).

1
QaAutomator

Wenn Sie die beiden Listen sortieren und dann die SequenceEqual-Methode zurückgeben, können Sie dies in drei Codezeilen tun. SequenceEqual gibt zurück, ob zwei Listen dieselben Elemente in derselben Reihenfolge haben (daher die Sortierung vor dem Vergleich).

    internal bool DoIdsMatchThoseFromXml(List<string> Ids, List<string> XmlIds)
    {
        Ids.Sort();
        XmlIds.Sort();

        return Ids.SequenceEqual(XmlIds);
    }