475 Stimmen

Liste in Unterlisten aufteilen mit LINQ

Gibt es eine Möglichkeit, wie ich eine List<SomeObject> in mehrere separate Listen von SomeObject und dabei den Artikelindex als Trennzeichen für jeden Split?

Lassen Sie mich ein Beispiel geben:

Ich habe eine List<SomeObject> und ich brauche eine List<List<SomeObject>> o List<SomeObject>[] , so dass jede dieser resultierenden Listen eine Gruppe von 3 Elementen der ursprünglichen Liste (in der Reihenfolge) enthält.

z. B.:

  • Ursprüngliche Liste: [a, g, e, w, p, s, q, f, x, y, i, m, c]

  • Daraus resultierende Listen: [a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]

Außerdem muss die Größe der resultierenden Liste ein Parameter dieser Funktion sein.

5voto

Robert McKee Punkte 20868

Veralteter Code, aber das ist, was ich benutzt habe:

    public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        var toReturn = new List<T>(max);
        foreach (var item in source)
        {
            toReturn.Add(item);
            if (toReturn.Count == max)
            {
                yield return toReturn;
                toReturn = new List<T>(max);
            }
        }
        if (toReturn.Any())
        {
            yield return toReturn;
        }
    }

5voto

aolszowka Punkte 1120

Dies ist eine alte Frage, aber das ist, was ich am Ende mit; es enumeriert die enumerable nur einmal, aber Listen für jede der Partitionen erstellen. Es leidet nicht unter unerwartetem Verhalten, wenn ToArray() aufgerufen wird, wie es einige der Implementierungen tun:

    public static IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int chunkSize)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }

        if (chunkSize < 1)
        {
            throw new ArgumentException("Invalid chunkSize: " + chunkSize);
        }

        using (IEnumerator<T> sourceEnumerator = source.GetEnumerator())
        {
            IList<T> currentChunk = new List<T>();
            while (sourceEnumerator.MoveNext())
            {
                currentChunk.Add(sourceEnumerator.Current);
                if (currentChunk.Count == chunkSize)
                {
                    yield return currentChunk;
                    currentChunk = new List<T>();
                }
            }

            if (currentChunk.Any())
            {
                yield return currentChunk;
            }
        }
    }

5voto

Roman Pekar Punkte 98979

Was ist mit dem hier?

var input = new List<string> { "a", "g", "e", "w", "p", "s", "q", "f", "x", "y", "i", "m", "c" };
var k = 3

var res = Enumerable.Range(0, (input.Count - 1) / k + 1)
                    .Select(i => input.GetRange(i * k, Math.Min(k, input.Count - i * k)))
                    .ToList();

Soviel ich weiß, GetRange() ist linear in Bezug auf die Anzahl der entnommenen Gegenstände. Dies sollte also gut funktionieren.

5voto

mwjackson Punkte 5313

Wir fanden, dass die Lösung von David B. am besten funktioniert. Aber wir haben sie an eine allgemeinere Lösung angepasst:

list.GroupBy(item => item.SomeProperty) 
   .Select(group => new List<T>(group)) 
   .ToArray();

4voto

Die folgende Lösung ist die kompakteste, die mir einfiel und die O(n) ist.

public static IEnumerable<T[]> Chunk<T>(IEnumerable<T> source, int chunksize)
{
    var list = source as IList<T> ?? source.ToList();
    for (int start = 0; start < list.Count; start += chunksize)
    {
        T[] chunk = new T[Math.Min(chunksize, list.Count - start)];
        for (int i = 0; i < chunk.Length; i++)
            chunk[i] = list[start + i];

        yield return chunk;
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X