Есть ли хороший способ разбить коллекцию на n
части с помощью LINQ? Конечно, не обязательно равномерно.
То есть я хочу разделить коллекцию на подколлекции, каждая из которых содержит подмножество элементов, причем последняя коллекция может быть разорвана.
c#
.net
linq
data-structures
Simon_Weaver
источник
источник
Ответы:
Чистый linq и самое простое решение показано ниже.
источник
.AsEnumerable()
не является обязательным, IGrouping <T> уже является IEnumerable <T>.РЕДАКТИРОВАТЬ: Хорошо, похоже, я неправильно понял вопрос. Я прочитал это как «кусочки длины n», а не «n штук». Doh! Рассматривая вопрос об удалении ответа ...
(Оригинальный ответ)
Я не верю, что есть встроенный способ разделения, хотя я намерен написать его в своем наборе дополнений к LINQ to Objects. У Марка Гравелла есть реализация здесь, хотя я, вероятно, изменил бы ее, чтобы вернуть представление только для чтения:
источник
yield return
. Для этого требуется, чтобы в памяти находился только один пакет , но это все.источник
var dept = {1,2,3,4,5}
. После разбивки результат вродеdept1 = {1,3,5}
иdept2 = { 2,4 }
гдеparts = 2
. Но результат, который мне нужен,dept1 = {1,2,3}
иdept2 = {4,5}
int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);
затем сделал деление с помощью.GroupBy(x => x.index / columnLength)
. Один из недостатков - Count () перечисляет список.Хорошо, я брошу шляпу на ринг. Преимущества моего алгоритма:
Код:
Как указано в комментариях ниже, этот подход на самом деле не решает исходный вопрос, который требовал фиксированного количества разделов примерно одинаковой длины. Тем не менее, вы все еще можете использовать мой подход для решения исходного вопроса, назвав его так:
При таком использовании подход больше не O (1), поскольку операция Count () равна O (N).
источник
Это то же самое, что и принятый ответ, но в гораздо более простом представлении:
Вышеупомянутый метод разбивает
IEnumerable<T>
на N частей равного или почти равного размера.Вышеупомянутый метод разбивает
IEnumerable<T>
на куски желаемого фиксированного размера, при этом общее количество кусков не имеет значения - вопрос не в этом.Проблема с
Split
методом, помимо того, что он медленнее, заключается в том, что он скремблирует вывод в том смысле, что группировка будет выполняться на основе i-го числа, кратного N для каждой позиции, или, другими словами, вы не получаете куски в исходном порядке.Почти каждый ответ здесь либо не сохраняет порядок, либо касается разделения, а не разделения, либо явно неверен. Попробуйте это быстрее, с сохранением порядка, но немного более подробным:
Эквивалентный метод
Partition
операции здесьисточник
Я довольно часто использую функцию разделения, которую опубликовал ранее. Единственная плохая вещь в том, что это не полностью потоковая передача. Это не проблема, если вы работаете с несколькими элементами в своей последовательности. Мне понадобилось новое решение, когда я начал работать с более чем 100 000 элементов в своей последовательности.
Следующее решение намного сложнее (и больше кода!), Но очень эффективно.
Наслаждайтесь!
источник
Интересная ветка. Чтобы получить потоковую версию Split / Partition, можно использовать перечислители и вывести последовательности из перечислителя с помощью методов расширения. Преобразование императивного кода в функциональный с помощью yield - действительно очень мощный метод.
Сначала расширение перечислителя, которое превращает количество элементов в ленивую последовательность:
И затем перечислимое расширение, которое разбивает последовательность:
Конечным результатом является высокоэффективная потоковая и ленивая реализация, основанная на очень простом коде.
Наслаждайтесь!
источник
Я использую это:
источник
Это эффективно с точки зрения памяти и максимально откладывает выполнение (на пакет) и работает за линейное время O (n)
источник
На этот вопрос (и его двоюродных братьев) есть много отличных ответов. Мне самому это было нужно, и я создал решение, которое должно быть эффективным и устойчивым к ошибкам в сценарии, когда исходную коллекцию можно рассматривать как список. Он не использует ленивую итерацию, поэтому может не подходить для коллекций неизвестного размера, которые могут оказывать давление на память.
Я видел несколько ответов на это семейство вопросов, использующих GetRange и Math.Min. Но я считаю, что в целом это более полное решение с точки зрения проверки ошибок и эффективности.
источник
источник
Отличные ответы, для своего сценария я проверил принятый ответ, и, похоже, он не соблюдает порядок. Есть также отличный ответ от Nawfal, который поддерживает порядок. Но в моем сценарии я хотел разделить остаток нормализованным способом, все ответы, которые я видел, распределяли остаток либо в начале, либо в конце.
В моем ответе также используется более нормализованное распределение остатка.
источник
Если порядок в этих частях не очень важен, вы можете попробовать следующее:
Однако по какой-то причине они не могут быть преобразованы в IEnumerable <IEnumerable <int>> ...
источник
Это мой код, красивый и короткий.
источник
Это мой способ: перечислять элементы и разбивать строки по столбцам
источник
Я искал разделение, подобное разделу со строкой, поэтому весь список разделен по какому-то правилу, а не только первая часть, это мое решение
источник
Вот небольшая настройка количества элементов вместо количества частей:
источник
источник
Только что наткнулся на эту ветку, и большинство решений здесь включают добавление элементов в коллекции, эффективно материализуя каждую страницу перед ее возвратом. Это плохо по двум причинам: во-первых, если ваши страницы большие, есть накладные расходы на память для заполнения страницы, во-вторых, есть итераторы, которые делают недействительными предыдущие записи при переходе к следующей (например, если вы обертываете DataReader в методе перечислителя) ,
В этом решении используются два вложенных метода перечислителя, чтобы избежать необходимости кэшировать элементы во временные коллекции. Поскольку внешний и внутренний итераторы проходят по одному и тому же перечислимому элементу, они обязательно используют один и тот же перечислитель, поэтому важно не продвигать внешний итераторы, пока вы не закончите обработку текущей страницы. Тем не менее, если вы решите не перебирать всю текущую страницу, когда вы перейдете на следующую страницу, это решение автоматически перейдет к границе страницы.
источник