Розділити колекцію на `n` частини за допомогою LINQ?


122

Чи є приємний спосіб розділити колекцію на nчастини за допомогою LINQ? Не обов’язково рівномірно, звичайно.

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


1
Retagged: Питання не має нічого спільного з asp.net. Будь ласка, позначте свої питання належним чином.

Як саме ви хочете, щоб вони розкололися, якщо навіть не (зважаючи на кінець, звичайно)?
Марк Гравелл

1
хто пов’язав це питання? Джон це ти? :-) раптом усі ці відповіді :-)
Simon_Weaver


@Simon_Weaver Я намагався пояснити, що ви запитуєте, на основі прийнятої відповіді. Насправді, існує багато способів "розділити" список, включаючи декомпозицію кожного елемента списку на його елементи та внесення їх до так званих "паралельних" списків.
jpaugh

Відповіді:


127

Чистий зв'язок і найпростіший варіант, як показано нижче.

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 () замість вибору (IEnumerable <T>) частини. Він відчуває себе більш елегантним.
tuinstoel

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

8
Було б краще використовувати перевантаження Select, що включає індекс.
Marc Gravell

1
Я додав відповідь, що використовує синтаксис вибору перевантаження та методу
ланцюга

1
.AsEnumerable()не потрібно, IGrouping <T> - це вже IEnumerable <T>.
Олексій

58

EDIT: Гаразд, схоже, я неправильно прочитав питання. Я читаю це як "шматки довжиною n", а не "n штук". До! Розгляд видалення відповіді ...

(Оригінальна відповідь)

Я не вірю, що існує вбудований спосіб розділення, хоча я маю намір записати його в набір доповнень до LINQ до об’єктів. У Marc Gravell є реалізація тут, хоча я, ймовірно, модифікую її, щоб повернути вигляд лише для читання:

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

3
Вам дуже не подобаються ті "масиви [count ++]", eh
;-p

18
Дякую за те, що ви не видалили, хоча це не відповідь для ОП, я хотів точно таке - шматки довжиною n :).
Гішу

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, тому це моя улюблена відповідь.
пієдар

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}
Картік Arthik

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

24

Гаразд, я кину капелюшок на ринг. Переваги мого алгоритму:

  1. Немає дорогих операторів множення, ділення чи модулів
  2. Усі операції O (1) (див. Примітку нижче)
  3. Працює для IEnumerable <> source (не потрібне число властивостей)
  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). Звичайно, ви можете заперечити, що передбачення - це O (N) для початку ... :)
Майк

4
Ваша відповідь правильна, але питання до неї неправильне. Ваша відповідь дає невідому кількість шматок із фіксованим розміром для кожного шматка. Але ОП хоче розділити функціонал, де він дає фіксовану кількість шматочків з будь-яким розміром на шматок (сподіваємось, рівний або близький рівним розмірам). Можливо , більш підходить тут stackoverflow.com/questions/3773403 / ...
Навфал

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

1
@nawfal - Дякую за ваш детальний аналіз, він допомагає тримати мене на ногах. Загалом пов'язані списки відомі ефективними кінцевими вставками, саме тому я вибрав його тут. Однак я просто визначив це, і перелік справді набагато швидший. Я підозрюю, що це якась деталь реалізації .NET, можливо, заслуговує на окреме питання StackOverflow. Я змінив свою відповідь, щоб використовувати Список <> відповідно до вашої пропозиції. Попереднє розміщення ємності списку гарантує, що кінцеве вставлення все ще 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

Я використовував функцію Partition, яку я розміщував раніше досить часто. Єдиним поганим у цьому було те, що він не був повністю потоковим. Це не проблема, якщо ви працюєте з кількома елементами у своїй послідовності. Мені потрібно було нове рішення, коли я почав працювати зі 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, коли викликається Скидання - я вважаю, що багато методів розширення LINQ покладаються на таку поведінку.
ShadowChaser

1
@ShadowChaser Я думаю, що Reset () повинен кинути NotSupportedException, і все буде добре. З документації MSDN: "Метод Reset передбачений для сумісності COM. Він не обов'язково повинен бути реалізований; натомість реалізатор може просто кинути NotSupportedException."
Toong

@toong Wow, ти маєш рацію. Не впевнений, як я пропустив це після всього цього часу.
ShadowChaser

Це баггі! Я точно не пам’ятаю, але (наскільки я пам’ятаю) він виконує небажаний крок, і це може призвести до некрасивих побічних ефектів (з перечитачем даних, наприклад). Найкраще рішення - тут (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/…
SalientBrain

4

Цікава нитка. Щоб отримати поточну версію Split / Partition, можна використовувати перечислювачі та виводити послідовності з переписувача, використовуючи методи розширення. Перетворення імперативного коду у функціональний код за допомогою виходу дійсно є дуже потужною технікою.

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

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 розділів з п елементами в первісному переліченні, а внутрішні перелічі дадуть несподівані результати
Бред

@ Бред буде «глючити» , як ви очікуєте, схожий на деякі з питань у цій темі 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));
}

Поясніть, будь ласка, чому. Я без жодних проблем використовував цю функцію!
Elmer

прочитайте запитання ще раз і побачите, чи отримаєте n (майже) частини однакової довжини за своєю функцією
Мухаммед Хасан Хан

@Elmer Ваша відповідь правильна, але питання для неї неправильне. Ваша відповідь дає невідому кількість фрагментів з фіксованим розміром для кожного елемента (саме як Partition, ім'я, яке ви вказали для цього). Але ОП хоче розділити функціонал, де він дає фіксовану кількість шматочків з будь-яким розміром на шматок (сподіваємось, рівний або близький рівним розмірам). Можливо , більш підходить тут stackoverflow.com/questions/3773403 / ...
Навфал

Я думаю, ви можете просто змінити 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

Чудові відповіді, для мого сценарію я перевірив прийняту відповідь, і, здається, це не підтримує порядок. також є чудова відповідь Навфал, яка підтримує порядок. Але в моєму сценарії я хотів розділити решту нормалізованим способом, а всі відповіді, які я бачив, поширювали решту або на початку, або в кінці.

Моя відповідь також передбачає, що залишок поширюється більш нормалізованим способом.

 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.