c# - verkettete - zirkuläre liste c




Schnellste Möglichkeit, gängige Objekte in mehreren Listen in C#zu finden (7)

Ok, das wird die Liste der Optionsobjekte finden, die einen Wert in jeder Liste haben.

var x = from list in optionLists
        from option in list
        where optionLists.All(l => l.Any(o => o.Value == option.Value))
        orderby option.Value
        select option;

Es wird keine "distinct" -Auswahl vorgenommen, daher werden mehrere Optionsobjekte zurückgegeben, von denen einige denselben Wert haben.

Angesichts der folgenden:

List<List<Option>> optionLists;

Was wäre ein schneller Weg, um die Teilmenge der Option-Objekte zu bestimmen, die in allen N Listen erscheinen? Die Gleichheit wird durch eine Zeichenfolgeeigenschaft wie option1.Value == option2.Value bestimmt.

Also sollten wir mit List<Option> enden, wo jedes Element nur einmal erscheint.


Sortiere und tue dann etwas, das einer Mischsortierung ähnelt.

Im Grunde würden Sie das tun:

  1. Rufen Sie das erste Element aus jeder Liste ab
  2. Vergleichen Sie die Items, wenn sie gleich sind
  3. Wenn eines der Elemente vor den anderen sortiert ist, rufen Sie ein neues Element aus der entsprechenden Liste ab, um es zu ersetzen. Andernfalls rufen Sie neue Elemente ab, um sie alle aus der Liste zu ersetzen
  4. Solange du noch Gegenstände hast, gehe zurück zu 2.

Ich habe keine Leistungsstatistiken, aber wenn Sie Ihre eigene Methode nicht rollen wollen, haben verschiedene Sammlungsbibliotheken ein 'Set' oder 'Set (T)' Objekt, das die üblichen Set-Prozeduren bietet. (aufgelistet in der Reihenfolge, in der ich sie benutzen würde).

  1. IESI-Sammlungen (wörtlich nur Klassen setzen)
  2. PowerCollections (seit einiger Zeit nicht mehr aktualisiert)
  3. C5 (nie persönlich benutzt)

Sie können dies tun, indem Sie das Vorkommen aller Elemente in allen Listen zählen - die Elemente, deren Häufigkeitsanzahl der Anzahl der Listen entspricht, sind allen Listen gemeinsam:

    static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
    {
        Dictionary<T, int> map = new Dictionary<T, int>();
        int listCount = 0; // number of lists

        foreach (IEnumerable<T> list in lists)
        {
            listCount++;
            foreach (T item in list)
            {
                // Item encountered, increment count
                int currCount;
                if (!map.TryGetValue(item, out currCount))
                    currCount = 0;

                currCount++;
                map[item] = currCount;
            }
        }

        List<T> result= new List<T>();
        foreach (KeyValuePair<T,int> kvp in map)
        {
            // Items whose occurrence count is equal to the number of lists are common to all the lists
            if (kvp.Value == listCount)
                result.Add(kvp.Key);
        }

        return result;
    }

Aufbauend auf Matts Antwort können wir, da wir nur an Optionen interessiert sind, die alle Listen gemeinsam haben, einfach nach Optionen in der ersten Liste suchen, die die anderen teilen:

var sharedOptions =
    from option in optionLists.First( ).Distinct( )
    where optionLists.Skip( 1 ).All( l => l.Contains( option ) )
    select option;

Wenn eine Optionsliste keine doppelten Ganzzahlen enthalten kann, ist der Distinct Aufruf nicht erforderlich. Wenn die Listen in der Größe stark variieren, wäre es besser, die Optionen in der kürzesten Liste zu durchlaufen, als in der Liste, die zufällig die First . Sortierte oder gehashte Auflistungen können verwendet werden, um die Suchzeit des Contains Aufrufs zu verbessern, obwohl dies für eine moderate Anzahl von Elementen keinen großen Unterschied machen sollte.


/// <summary>
    /// The method FindCommonItems, returns a list of all the COMMON ITEMS in the lists contained in the listOfLists.
    /// The method expects lists containing NO DUPLICATE ITEMS.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="allSets"></param>
    /// <returns></returns>
    public static List<T> FindCommonItems<T>(IEnumerable<List<T>> allSets)
    {
        Dictionary<T, int> map = new Dictionary<T, int>();
        int listCount = 0; // Number of lists.
        foreach (IEnumerable<T> currentSet in allSets)
        {
            int itemsCount = currentSet.ToList().Count;
            HashSet<T> uniqueItems = new HashSet<T>();
            bool duplicateItemEncountered = false;
            listCount++;
            foreach (T item in currentSet)
            {
                if (!uniqueItems.Add(item))
                {
                    duplicateItemEncountered = true;
                }                        
                if (map.ContainsKey(item))
                {
                    map[item]++;
                } 
                else
                {
                    map.Add(item, 1);
                }
            }
            if (duplicateItemEncountered)
            {
                uniqueItems.Clear();
                List<T> duplicateItems = new List<T>();
                StringBuilder currentSetItems = new StringBuilder();
                List<T> currentSetAsList = new List<T>(currentSet);
                for (int i = 0; i < itemsCount; i++)
                {
                    T currentItem = currentSetAsList[i];
                    if (!uniqueItems.Add(currentItem))
                    {
                        duplicateItems.Add(currentItem);
                    }
                    currentSetItems.Append(currentItem);
                    if (i < itemsCount - 1)
                    {
                        currentSetItems.Append(", ");
                    }
                }
                StringBuilder duplicateItemsNamesEnumeration = new StringBuilder();
                int j = 0;
                foreach (T item in duplicateItems)
                {
                    duplicateItemsNamesEnumeration.Append(item.ToString());
                    if (j < uniqueItems.Count - 1)
                    {
                        duplicateItemsNamesEnumeration.Append(", ");
                    }
                }
                throw new Exception("The list " + currentSetItems.ToString() + " contains the following duplicate items: " + duplicateItemsNamesEnumeration.ToString());
            }
        }
        List<T> result= new List<T>();
        foreach (KeyValuePair<T, int> itemAndItsCount in map)
        {
            if (itemAndItsCount.Value == listCount) // Items whose occurrence count is equal to the number of lists are common to all the lists.
            {
                result.Add(itemAndItsCount.Key);
            }
        }

        return result;
    }

Nachdem ich das Netz durchsucht hatte und mir nicht wirklich etwas einfallen ließ (oder das funktionierte), schlief ich darauf und kam auf diese Idee. Mein SearchResult ist ähnlich wie Ihre Option . Es hat eine EmployeeId und das ist die Sache, die ich in den Listen gemeinsam haben muss. Ich gebe alle Datensätze mit einer EmployeeId in jeder Liste zurück. Es ist nicht schick, aber es ist einfach und leicht zu verstehen, genau das, was ich mag. Für kleine Listen (mein Fall) sollte es gut funktionieren - und jeder kann es verstehen!

private List<SearchResult> GetFinalSearchResults(IEnumerable<IEnumerable<SearchResult>> lists)
{
    Dictionary<int, SearchResult> oldList = new Dictionary<int, SearchResult>();
    Dictionary<int, SearchResult> newList = new Dictionary<int, SearchResult>();

    oldList = lists.First().ToDictionary(x => x.EmployeeId, x => x);

    foreach (List<SearchResult> list in lists.Skip(1))
    {
        foreach (SearchResult emp in list)
        {
            if (oldList.Keys.Contains(emp.EmployeeId))
            {
                newList.Add(emp.EmployeeId, emp);
            }
        }

        oldList = new Dictionary<int, SearchResult>(newList);
        newList.Clear();
    }

    return oldList.Values.ToList();
}






c#