разделение списка на несколько списков в C # - PullRequest
9 голосов
/ 05 ноября 2010

У меня есть список строк, которые я отправляю в очередь. Мне нужно разделить список, чтобы я получил список списков, в котором каждый список содержит максимальное (определяемое пользователем) количество строк. Например, если у меня есть список со следующими A, B, C, D, E, F, G, H, I и максимальный размер списка равен 4, я хочу получить список списков, где первый элемент списка содержит: A, B, C, D, второй список содержит: E, F, G, H, а последний элемент списка содержит: I. Я посмотрел на функцию «TakeWhile», но не уверен, что это лучший подход. Любое решение для этого?

Ответы [ 3 ]

19 голосов
/ 05 ноября 2010

Вы можете установить List<IEnumerable<string>>, а затем использовать Skip и Take, чтобы разделить список:

IEnumerable<string> allStrings = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I" };

List<IEnumerable<string>> listOfLists = new List<IEnumerable<string>>();
for (int i = 0; i < allStrings.Count(); i += 4)
{                
    listOfLists.Add(allStrings.Skip(i).Take(4)); 
}

Теперь listOfLists будет содержать список списков.

17 голосов
/ 05 ноября 2010
/// <summary>
/// Splits a <see cref="List{T}"/> into multiple chunks.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list to be chunked.</param>
/// <param name="chunkSize">The size of each chunk.</param>
/// <returns>A list of chunks.</returns>
public static List<List<T>> SplitIntoChunks<T>(List<T> list, int chunkSize)
{
    if (chunkSize <= 0)
    {
        throw new ArgumentException("chunkSize must be greater than 0.");
    }

    List<List<T>> retVal = new List<List<T>>();
    int index = 0;
    while (index < list.Count)
    {
        int count = list.Count - index > chunkSize ? chunkSize : list.Count - index;
        retVal.Add(list.GetRange(index, count));

        index += chunkSize;
    }

    return retVal;
}

Ссылка: http://www.chinhdo.com/20080515/chunking/

2 голосов
/ 04 ноября 2015

Некоторые связанные чтения:

В противном случае незначительное отклонение принятый ответ для работы с перечислимыми (для ленивых)- загрузка и обработка, если список большой / дорогой).Я хотел бы отметить, что материализация каждого чанка / сегмента (например, через .ToList или .ToArray, или просто перечисление каждого чанка) может иметь побочные эффекты - см. Тесты.

Методы

// so you're not repeatedly counting an enumerable
IEnumerable<IEnumerable<T>> Chunk<T>(IEnumerable<T> list, int totalSize, int chunkSize) {
    int i = 0;
    while(i < totalSize) {
        yield return list.Skip(i).Take(chunkSize);
        i += chunkSize;
    }
}
// convenience for "countable" lists
IEnumerable<IEnumerable<T>> Chunk<T>(ICollection<T> list, int chunkSize) {
    return Chunk(list, list.Count, chunkSize);
}
IEnumerable<IEnumerable<T>> Chunk<T>(IEnumerable<T> list, int chunkSize) {
    return Chunk(list, list.Count(), chunkSize);
}

Тест (Linqpad)

(примечание: мне пришлось включить методы Assert для linqpad)

void Main()
{
    var length = 10;
    var size = 4;

    test(10, 4);
    test(10, 6);
    test(10, 2);
    test(10, 1);

    var sideeffects = Enumerable.Range(1, 10).Select(i => {
        string.Format("Side effect on {0}", i).Dump();
        return i;
    });

    "--------------".Dump("Before Chunking");
    var result = Chunk(sideeffects, 4);
    "--------------".Dump("After Chunking");
    result.Dump("SideEffects");
    var list = new List<int>();
    foreach(var segment in result) {
        list.AddRange(segment);
    }
    list.Dump("After crawling");

    var segment3 = result.Last().ToList();
    segment3.Dump("Last Segment");
}

// test
void test(int length, int size) {
    var list = Enumerable.Range(1, length);

    var c1 = Chunk(list, size);

    c1.Dump(string.Format("Results for [{0} into {1}]", length, size));

    Assert.AreEqual( (int) Math.Ceiling( (double)length / (double)size), c1.Count(), "Unexpected number of chunks");
    Assert.IsTrue(c1.All(c => c.Count() <= size), "Unexpected size of chunks");
}
...