Разбить коллекцию на n частей с помощью LINQ?


122

Есть ли хороший способ разбить коллекцию на nчасти с помощью LINQ? Конечно, не обязательно равномерно.

То есть я хочу разделить коллекцию на подколлекции, каждая из которых содержит подмножество элементов, причем последняя коллекция может быть разорвана.


1
С пометкой: вопрос не имеет ничего общего с asp.net. Пожалуйста, отметьте свои вопросы соответствующим образом.

Как именно вы хотите, чтобы они разделились, если не даже (с учетом конца, конечно)?
Марк Гравелл

1
кто связался с этим вопросом? Джон это ты? :-) вдруг все эти ответы :-)
Simon_Weaver


@Simon_Weaver Я попытался уточнить, о чем вы спрашиваете, на основе принятого ответа. Фактически, существует множество способов «разбить» список, включая разложение каждого элемента списка на его элементы и помещение их в так называемые «параллельные» списки.
jpaugh

Ответы:


127

Чистый linq и самое простое решение показано ниже.

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}

3
Вы можете: выбрать part.AsEnumerable () вместо select (IEnumerable <T>) part. Он кажется более элегантным.
Tuinstoel

2
Выполнение всех этих операций с модулями для длинных списков может стать немного дороже.
Джонатан Аллен,

8
Было бы лучше использовать перегрузку Select, которая включает index.
Марк Гравелл

1
Я добавил ответ, в котором используется синтаксис выбора перегрузки и цепочки методов
reustmd

1
.AsEnumerable()не является обязательным, IGrouping <T> уже является IEnumerable <T>.
Alex

58

РЕДАКТИРОВАТЬ: Хорошо, похоже, я неправильно понял вопрос. Я прочитал это как «кусочки длины n», а не «n штук». Doh! Рассматривая вопрос об удалении ответа ...

(Оригинальный ответ)

Я не верю, что есть встроенный способ разделения, хотя я намерен написать его в своем наборе дополнений к LINQ to Objects. У Марка Гравелла есть реализация здесь, хотя я, вероятно, изменил бы ее, чтобы вернуть представление только для чтения:

public static IEnumerable<IEnumerable<T>> Partition<T>
    (this IEnumerable<T> source, int size)
{
    T[] array = null;
    int count = 0;
    foreach (T item in source)
    {
        if (array == null)
        {
            array = new T[size];
        }
        array[count] = item;
        count++;
        if (count == size)
        {
            yield return new ReadOnlyCollection<T>(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection<T>(array);
    }
}

Черт возьми, опереди меня ;-p
Marc Gravell

3
Вам действительно не нравятся эти "array [count ++]", а ;-p
Марк

18
Спасибо, что не удалили, хотя это не ответ для OP, я хотел то же самое - кусочки длины n :).
Gishu

2
@Dejan: Нет, это не так. Обратите внимание на использование yield return. Для этого требуется, чтобы в памяти находился только один пакет , но это все.
Джон Скит

1
@Dejan: Верно - я бы не хотел догадываться о том, как он взаимодействует с параллельным секционированием LINQ, если честно :)
Джон Скит

39
static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
            return list.Select((item, index) => new {index, item})
                       .GroupBy(x => x.index % parts)
                       .Select(x => x.Select(y => y.item));
    }
}

28
У меня иррациональная неприязнь к Linq в стиле SQL, так что это мой любимый ответ.
piedar 02

1
@ manu08, я пробовал твой код, у меня есть список var dept = {1,2,3,4,5}. После разбивки результат вроде dept1 = {1,3,5}и dept2 = { 2,4 }где parts = 2. Но результат, который мне нужен, dept1 = {1,2,3}иdept2 = {4,5}
Картик Артик

3
У меня была такая же проблема с модулем, поэтому я рассчитал длину столбца с помощью, а int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);затем сделал деление с помощью .GroupBy(x => x.index / columnLength). Один из недостатков - Count () перечисляет список.
goodeye 03

24

Хорошо, я брошу шляпу на ринг. Преимущества моего алгоритма:

  1. Отсутствие дорогостоящих операторов умножения, деления или модуля
  2. Все операции O (1) (см. Примечание ниже)
  3. Работает для источника IEnumerable <> (свойство Count не требуется)
  4. просто

Код:

public static IEnumerable<IEnumerable<T>>
  Section<T>(this IEnumerable<T> source, int length)
{
  if (length <= 0)
    throw new ArgumentOutOfRangeException("length");

  var section = new List<T>(length);

  foreach (var item in source)
  {
    section.Add(item);

    if (section.Count == length)
    {
      yield return section.AsReadOnly();
      section = new List<T>(length);
    }
  }

  if (section.Count > 0)
    yield return section.AsReadOnly();
}

Как указано в комментариях ниже, этот подход на самом деле не решает исходный вопрос, который требовал фиксированного количества разделов примерно одинаковой длины. Тем не менее, вы все еще можете использовать мой подход для решения исходного вопроса, назвав его так:

myEnum.Section(myEnum.Count() / number_of_sections + 1)

При таком использовании подход больше не O (1), поскольку операция Count () равна O (N).


Великолепно - лучшее решение здесь! Несколько оптимизаций: * Очистите связанный список вместо создания нового для каждого раздела. Ссылка на связанный список никогда не возвращается вызывающему, поэтому это полностью безопасно. * Не создавайте связанный список, пока не дойдете до первого элемента - в этом случае не будет выделения, если источник пуст
ShadowChaser

3
@ShadowChaser Согласно MSDN очистка LinkedList - это сложность O (N), поэтому это разрушит мою цель O (1). Конечно, вы можете возразить, что foreach - это O (N) для начала ... :)
Майк,

4
ваш ответ правильный, но вопрос неправильный. Ваш ответ дает неизвестное количество фрагментов с фиксированным размером для каждого фрагмента. Но OP хочет функциональность Split, которая дает фиксированное количество фрагментов с любым размером на кусок (надеюсь, равных или близких к одинаковым размерам). Возможно, здесь больше подходит stackoverflow.com/questions/3773403/…
nawfal 06

1
@ Майк, ты проверил это? Надеюсь, вы знаете, что O (1) не означает быстрее, это означает только то, что время, необходимое для разделения, не масштабируется. Мне просто интересно, каково ваше обоснование слепо придерживаться O (1), когда оно может быть медленнее, чем другие O (n) для всех сценариев реальной жизни. Я даже протестировал его на сумасшедший список силы 10 ^ 8, и мой оказался еще быстрее. Надеюсь, вы знаете, что не существует даже стандартных типов коллекций, которые могут содержать 10 ^ 12 элементов ..
nawfal

1
@nawfal - Спасибо за подробный анализ, он меня держит в тонусе. Связанные списки в целом известны эффективными концевыми вставками, поэтому я выбрал их здесь. Однако я только что протестировал его, и действительно, List <> намного быстрее. Я подозреваю, что это какая-то деталь реализации .NET, возможно, заслуживающая отдельного вопроса о StackOverflow. Я изменил свой ответ, чтобы использовать List <> в соответствии с вашим предложением. Предварительное выделение емкости списка гарантирует, что конечная вставка по-прежнему будет O (1) и соответствует моей первоначальной цели дизайна. Я также перешел на встроенный .AsReadOnly () в .NET 4.5.
Майк

16

Это то же самое, что и принятый ответ, но в гораздо более простом представлении:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}

Вышеупомянутый метод разбивает IEnumerable<T>на N частей равного или почти равного размера.

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

Вышеупомянутый метод разбивает IEnumerable<T> на куски желаемого фиксированного размера, при этом общее количество кусков не имеет значения - вопрос не в этом.

Проблема с Split методом, помимо того, что он медленнее, заключается в том, что он скремблирует вывод в том смысле, что группировка будет выполняться на основе i-го числа, кратного N для каждой позиции, или, другими словами, вы не получаете куски в исходном порядке.

Почти каждый ответ здесь либо не сохраняет порядок, либо касается разделения, а не разделения, либо явно неверен. Попробуйте это быстрее, с сохранением порядка, но немного более подробным:

public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, 
                                                   int numberOfChunks)
{
    if (numberOfChunks <= 0 || numberOfChunks > items.Count)
        throw new ArgumentOutOfRangeException("numberOfChunks");

    int sizePerPacket = items.Count / numberOfChunks;
    int extra = items.Count % numberOfChunks;

    for (int i = 0; i < numberOfChunks - extra; i++)
        yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);

    int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
    int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
    for (int i = 0; i < extra; i++)
        yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}

Эквивалентный метод Partitionоперации здесь


6

Я довольно часто использую функцию разделения, которую опубликовал ранее. Единственная плохая вещь в том, что это не полностью потоковая передача. Это не проблема, если вы работаете с несколькими элементами в своей последовательности. Мне понадобилось новое решение, когда я начал работать с более чем 100 000 элементов в своей последовательности.

Следующее решение намного сложнее (и больше кода!), Но очень эффективно.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace LuvDaSun.Linq
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int partitionSize)
        {
            /*
            return enumerable
                .Select((item, index) => new { Item = item, Index = index, })
                .GroupBy(item => item.Index / partitionSize)
                .Select(group => group.Select(item => item.Item)                )
                ;
            */

            return new PartitioningEnumerable<T>(enumerable, partitionSize);
        }

    }


    class PartitioningEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        IEnumerable<T> _enumerable;
        int _partitionSize;
        public PartitioningEnumerable(IEnumerable<T> enumerable, int partitionSize)
        {
            _enumerable = enumerable;
            _partitionSize = partitionSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new PartitioningEnumerator<T>(_enumerable.GetEnumerator(), _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitioningEnumerator<T> : IEnumerator<IEnumerable<T>>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitioningEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
            _enumerator.Dispose();
        }

        IEnumerable<T> _current;
        public IEnumerable<T> Current
        {
            get { return _current; }
        }
        object IEnumerator.Current
        {
            get { return _current; }
        }

        public void Reset()
        {
            _current = null;
            _enumerator.Reset();
        }

        public bool MoveNext()
        {
            bool result;

            if (_enumerator.MoveNext())
            {
                _current = new PartitionEnumerable<T>(_enumerator, _partitionSize);
                result = true;
            }
            else
            {
                _current = null;
                result = false;
            }

            return result;
        }

    }



    class PartitionEnumerable<T> : IEnumerable<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitionEnumerable(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new PartitionEnumerator<T>(_enumerator, _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitionEnumerator<T> : IEnumerator<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        int _count;
        public PartitionEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
        }

        public T Current
        {
            get { return _enumerator.Current; }
        }
        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }
        public void Reset()
        {
            if (_count > 0) throw new InvalidOperationException();
        }

        public bool MoveNext()
        {
            bool result;

            if (_count < _partitionSize)
            {
                if (_count > 0)
                {
                    result = _enumerator.MoveNext();
                }
                else
                {
                    result = true;
                }
                _count++;
            }
            else
            {
                result = false;
            }

            return result;
        }

    }
}

Наслаждайтесь!


Эта версия нарушает договор IEnumerator. Недопустимо генерировать InvalidOperationException при вызове Reset - я считаю, что многие методы расширения LINQ полагаются на это поведение.
ShadowChaser

1
@ShadowChaser Я думаю, что Reset () должен выбросить NotSupportedException, и все будет в порядке. Из документации MSDN: «Метод Reset предоставляется для взаимодействия с COM. Его не обязательно реализовывать; вместо этого разработчик может просто выбросить NotSupportedException».
toong

@toong Вау, ты прав. Не знаю, как я это пропустил после всего этого времени.
ShadowChaser

Глючит! Я точно не помню, но (насколько я помню) он выполняет нежелательный шаг и может привести к неприятным побочным эффектам (например, с помощью datareader). Лучшее решение здесь (Джеппе Стиг Нильсен): stackoverflow.com/questions/13709626/…
SalientBrain

4

Интересная ветка. Чтобы получить потоковую версию Split / Partition, можно использовать перечислители и вывести последовательности из перечислителя с помощью методов расширения. Преобразование императивного кода в функциональный с помощью yield - действительно очень мощный метод.

Сначала расширение перечислителя, которое превращает количество элементов в ленивую последовательность:

public static IEnumerable<T> TakeFromCurrent<T>(this IEnumerator<T> enumerator, int count)
{
    while (count > 0)
    {
        yield return enumerator.Current;
        if (--count > 0 && !enumerator.MoveNext()) yield break;
    }
}

И затем перечислимое расширение, которое разбивает последовательность:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> seq, int partitionSize)
{
    var enumerator = seq.GetEnumerator();

    while (enumerator.MoveNext())
    {
        yield return enumerator.TakeFromCurrent(partitionSize);
    }
}

Конечным результатом является высокоэффективная потоковая и ленивая реализация, основанная на очень простом коде.

Наслаждайтесь!


Изначально я запрограммировал то же самое, но шаблон ломается, когда Reset вызывается в одном из вложенных экземпляров IEnumerable <T>.
ShadowChaser

1
Это все еще работает, если вы перечисляете только раздел, а не внутреннее перечисление? поскольку внутренний перечислитель отложен, ни один код для внутреннего (взять из текущего) не будет выполняться до тех пор, пока он не будет перечислен, поэтому movenext () будет вызываться только функцией внешнего раздела, верно? Если мои предположения верны, то это потенциально может привести к n разделам с n элементами в исходном перечислимом, а внутренние перечисления дадут неожиданные результаты,
Брэд

@Brad, как и следовало ожидать, он «выйдет из строя», аналогично некоторым проблемам в этом потоке stackoverflow.com/questions/419019/… (в частности, stackoverflow.com/a/20953521/1037948 )
drzaus

4

Я использую это:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> instance, int partitionSize)
{
    return instance
        .Select((value, index) => new { Index = index, Value = value })
        .GroupBy(i => i.Index / partitionSize)
        .Select(i => i.Select(i2 => i2.Value));
}

Объясните, пожалуйста, почему. Я без проблем пользуюсь этой функцией!
Элмер

прочтите вопрос еще раз и посмотрите, получите ли вы n (почти) частей одинаковой длины с вашей функцией
Мухаммад Хасан Хан

@Elmer, ваш ответ правильный, но вопрос неправильный. Ваш ответ дает неизвестное количество фрагментов с фиксированным размером для каждого фрагмента (точно так же, как Раздел, имя, которое вы ему дали). Но OP хочет функциональность Split, которая дает фиксированное количество фрагментов с любым размером на кусок (надеюсь, равных или близких к одинаковым размерам). Возможно, здесь больше подходит stackoverflow.com/questions/3773403/…
nawfal 06

Я думаю, вы можете просто изменить i.Index / partitionSize на i.Index% partitionSize и получить запрошенный результат. Я также предпочитаю это принятому ответу, поскольку он более компактный и читаемый.
Джейк Дрю

2

Это эффективно с точки зрения памяти и максимально откладывает выполнение (на пакет) и работает за линейное время O (n)

    public static IEnumerable<IEnumerable<T>> InBatchesOf<T>(this IEnumerable<T> items, int batchSize)
    {
        List<T> batch = new List<T>(batchSize);
        foreach (var item in items)
        {
            batch.Add(item);

            if (batch.Count >= batchSize)
            {
                yield return batch;
                batch = new List<T>();
            }
        }

        if (batch.Count != 0)
        {
            //can't be batch size or would've yielded above
            batch.TrimExcess();
            yield return batch;
        }
    }

2

На этот вопрос (и его двоюродных братьев) есть много отличных ответов. Мне самому это было нужно, и я создал решение, которое должно быть эффективным и устойчивым к ошибкам в сценарии, когда исходную коллекцию можно рассматривать как список. Он не использует ленивую итерацию, поэтому может не подходить для коллекций неизвестного размера, которые могут оказывать давление на память.

static public IList<T[]> GetChunks<T>(this IEnumerable<T> source, int batchsize)
{
    IList<T[]> result = null;
    if (source != null && batchsize > 0)
    {
        var list = source as List<T> ?? source.ToList();
        if (list.Count > 0)
        {
            result = new List<T[]>();
            for (var index = 0; index < list.Count; index += batchsize)
            {
                var rangesize = Math.Min(batchsize, list.Count - index);
                result.Add(list.GetRange(index, rangesize).ToArray());
            }
        }
    }
    return result ?? Enumerable.Empty<T[]>().ToList();
}

static public void TestGetChunks()
{
    var ids = Enumerable.Range(1, 163).Select(i => i.ToString());
    foreach (var chunk in ids.GetChunks(20))
    {
        Console.WriteLine("[{0}]", String.Join(",", chunk));
    }
}

Я видел несколько ответов на это семейство вопросов, использующих GetRange и Math.Min. Но я считаю, что в целом это более полное решение с точки зрения проверки ошибок и эффективности.


1
   protected List<List<int>> MySplit(int MaxNumber, int Divider)
        {
            List<List<int>> lst = new List<List<int>>();
            int ListCount = 0;
            int d = MaxNumber / Divider;
            lst.Add(new List<int>());
            for (int i = 1; i <= MaxNumber; i++)
            {
                lst[ListCount].Add(i);
                if (i != 0 && i % d == 0)
                {
                    ListCount++;
                    d += MaxNumber / Divider;
                    lst.Add(new List<int>());
                }
            }
            return lst;
        }

1

Отличные ответы, для своего сценария я проверил принятый ответ, и, похоже, он не соблюдает порядок. Есть также отличный ответ от Nawfal, который поддерживает порядок. Но в моем сценарии я хотел разделить остаток нормализованным способом, все ответы, которые я видел, распределяли остаток либо в начале, либо в конце.

В моем ответе также используется более нормализованное распределение остатка.

 static class Program
{          
    static void Main(string[] args)
    {
        var input = new List<String>();
        for (int k = 0; k < 18; ++k)
        {
            input.Add(k.ToString());
        }
        var result = splitListIntoSmallerLists(input, 15);            
        int i = 0;
        foreach(var resul in result){
            Console.WriteLine("------Segment:" + i.ToString() + "--------");
            foreach(var res in resul){
                Console.WriteLine(res);
            }
            i++;
        }
        Console.ReadLine();
    }

    private static List<List<T>> splitListIntoSmallerLists<T>(List<T> i_bigList,int i_numberOfSmallerLists)
    {
        if (i_numberOfSmallerLists <= 0)
            throw new ArgumentOutOfRangeException("Illegal value of numberOfSmallLists");

        int normalizedSpreadRemainderCounter = 0;
        int normalizedSpreadNumber = 0;
        //e.g 7 /5 > 0 ==> output size is 5 , 2 /5 < 0 ==> output is 2          
        int minimumNumberOfPartsInEachSmallerList = i_bigList.Count / i_numberOfSmallerLists;                        
        int remainder = i_bigList.Count % i_numberOfSmallerLists;
        int outputSize = minimumNumberOfPartsInEachSmallerList > 0 ? i_numberOfSmallerLists : remainder;
        //In case remainder > 0 we want to spread the remainder equally between the others         
        if (remainder > 0)
        {
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                normalizedSpreadNumber = (int)Math.Floor((double)i_numberOfSmallerLists / remainder);    
            }
            else
            {
                normalizedSpreadNumber = 1;
            }   
        }
        List<List<T>> retVal = new List<List<T>>(outputSize);
        int inputIndex = 0;            
        for (int i = 0; i < outputSize; ++i)
        {
            retVal.Add(new List<T>());
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                retVal[i].AddRange(i_bigList.GetRange(inputIndex, minimumNumberOfPartsInEachSmallerList));
                inputIndex += minimumNumberOfPartsInEachSmallerList;
            }
            //If we have remainder take one from it, if our counter is equal to normalizedSpreadNumber.
            if (remainder > 0)
            {
                if (normalizedSpreadRemainderCounter == normalizedSpreadNumber-1)
                {
                    retVal[i].Add(i_bigList[inputIndex]);
                    remainder--;
                    inputIndex++;
                    normalizedSpreadRemainderCounter=0;
                }
                else
                {
                    normalizedSpreadRemainderCounter++;
                }
            }
        }
        return retVal;
    }      

}

0

Если порядок в этих частях не очень важен, вы можете попробовать следующее:

int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int n = 3;

var result =
   array.Select((value, index) => new { Value = value, Index = index }).GroupBy(i => i.Index % n, i => i.Value);

// or
var result2 =
   from i in array.Select((value, index) => new { Value = value, Index = index })
   group i.Value by i.Index % n into g
   select g;

Однако по какой-то причине они не могут быть преобразованы в IEnumerable <IEnumerable <int>> ...


Это может быть сделано. Вместо прямого литья просто сделайте функцию универсальной, а затем вызовите ее для своего массива int
nawfal

0

Это мой код, красивый и короткий.

 <Extension()> Public Function Chunk(Of T)(ByVal this As IList(Of T), ByVal size As Integer) As List(Of List(Of T))
     Dim result As New List(Of List(Of T))
     For i = 0 To CInt(Math.Ceiling(this.Count / size)) - 1
         result.Add(New List(Of T)(this.GetRange(i * size, Math.Min(size, this.Count - (i * size)))))
     Next
     Return result
 End Function

0

Это мой способ: перечислять элементы и разбивать строки по столбцам

  int repat_count=4;

  arrItems.ForEach((x, i) => {
    if (i % repat_count == 0) 
        row = tbo.NewElement(el_tr, cls_min_height);
    var td = row.NewElement(el_td);
    td.innerHTML = x.Name;
  });

0

Я искал разделение, подобное разделу со строкой, поэтому весь список разделен по какому-то правилу, а не только первая часть, это мое решение

List<int> sequence = new List<int>();
for (int i = 0; i < 2000; i++)
{
     sequence.Add(i);
}
int splitIndex = 900;
List<List<int>> splitted = new List<List<int>>();
while (sequence.Count != 0)
{
    splitted.Add(sequence.Take(splitIndex).ToList() );
    sequence.RemoveRange(0, Math.Min(splitIndex, sequence.Count));
}

В следующий раз попробуйте: var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros

0

Вот небольшая настройка количества элементов вместо количества частей:

public static class MiscExctensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int nbItems)
    {
        return (
            list
            .Select((o, n) => new { o, n })
            .GroupBy(g => (int)(g.n / nbItems))
            .Select(g => g.Select(x => x.o))
        );
    }
}

-1
int[] items = new int[] { 0,1,2,3,4,5,6,7,8,9, 10 };

int itemIndex = 0;
int groupSize = 2;
int nextGroup = groupSize;

var seqItems = from aItem in items
               group aItem by 
                            (itemIndex++ < nextGroup) 
                            ? 
                            nextGroup / groupSize
                            :
                            (nextGroup += groupSize) / groupSize
                            into itemGroup
               select itemGroup.AsEnumerable();

-1

Только что наткнулся на эту ветку, и большинство решений здесь включают добавление элементов в коллекции, эффективно материализуя каждую страницу перед ее возвратом. Это плохо по двум причинам: во-первых, если ваши страницы большие, есть накладные расходы на память для заполнения страницы, во-вторых, есть итераторы, которые делают недействительными предыдущие записи при переходе к следующей (например, если вы обертываете DataReader в методе перечислителя) ,

В этом решении используются два вложенных метода перечислителя, чтобы избежать необходимости кэшировать элементы во временные коллекции. Поскольку внешний и внутренний итераторы проходят по одному и тому же перечислимому элементу, они обязательно используют один и тот же перечислитель, поэтому важно не продвигать внешний итераторы, пока вы не закончите обработку текущей страницы. Тем не менее, если вы решите не перебирать всю текущую страницу, когда вы перейдете на следующую страницу, это решение автоматически перейдет к границе страницы.

using System.Collections.Generic;

public static class EnumerableExtensions
{
    /// <summary>
    /// Partitions an enumerable into individual pages of a specified size, still scanning the source enumerable just once
    /// </summary>
    /// <typeparam name="T">The element type</typeparam>
    /// <param name="enumerable">The source enumerable</param>
    /// <param name="pageSize">The number of elements to return in each page</param>
    /// <returns></returns>
    public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int pageSize)
    {
        var enumerator = enumerable.GetEnumerator();

        while (enumerator.MoveNext())
        {
            var indexWithinPage = new IntByRef { Value = 0 };

            yield return SubPartition(enumerator, pageSize, indexWithinPage);

            // Continue iterating through any remaining items in the page, to align with the start of the next page
            for (; indexWithinPage.Value < pageSize; indexWithinPage.Value++)
            {
                if (!enumerator.MoveNext())
                {
                    yield break;
                }
            }
        }
    }

    private static IEnumerable<T> SubPartition<T>(IEnumerator<T> enumerator, int pageSize, IntByRef index)
    {
        for (; index.Value < pageSize; index.Value++)
        {
            yield return enumerator.Current;

            if (!enumerator.MoveNext())
            {
                yield break;
            }
        }
    }

    private class IntByRef
    {
        public int Value { get; set; }
    }
}

Это вообще не работает! Лучшее из возможных - здесь stackoverflow.com/questions/13709626/… ! См. Комментарии.
SalientBrain
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.