Есть ли способ, которым я могу разделить List<SomeObject>
на несколько отдельных списков SomeObject
, используя индекс элемента в качестве разделителя каждого разделения?
Позвольте мне привести пример:
У меня есть List<SomeObject>
и мне нужно List<List<SomeObject>>
или List<SomeObject>[]
, чтобы каждый из этих результирующих списков содержал группу из 3 элементов исходного списка (последовательно).
например.:
Оригинальный список:
[a, g, e, w, p, s, q, f, x, y, i, m, c]
Результирующие списки:
[a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]
Мне также нужно, чтобы размер результирующих списков был параметром этой функции.
источник
[a,g,e]
прежде чем перечислять еще какой-либо исходный список.GroupBy(x=>f(x)).First()
никогда не уступит группе. OP спросил о списках, но если мы напишем для работы с IEnumerable, сделав всего одну итерацию, мы получим преимущество в производительности.Этот вопрос немного старый, но я только что написал это, и я думаю, что он немного более элегантен, чем другие предлагаемые решения:
источник
if (chunksize <= 0) throw new ArgumentException("Chunk size must be greater than zero.", "chunksize");
O(n²)
. Вы можете перебрать список и получитьO(n)
время.source
заменяется завернутымIEnumerable
каждый раз. Таким образом, взятие элементовsource
проходит через слоиSkip
sВ целом, подход, предложенный CaseyB, работает нормально, на самом деле, если вы проходите мимо,
List<T>
его сложно обвинить , возможно, я бы изменил его на:Что позволит избежать массивных цепочек вызовов. Тем не менее, этот подход имеет общий недостаток. Он материализует два перечисления на чанк, чтобы выделить проблему, попробуйте выполнить:
Чтобы преодолеть это, мы можем попробовать подход Кэмерона , который проходит вышеупомянутый тест в полете цвета, поскольку он только идет по перечислению один раз.
Проблема в том, что у него есть другой недостаток, он материализует каждый элемент в каждом чанке, проблема с этим подходом заключается в том, что у вас много памяти.
Чтобы проиллюстрировать это, попробуйте запустить:
Наконец, любая реализация должна быть способна обрабатывать итеративные итерации блоков, например:
Многие очень оптимальные решения, такие как мой первый пересмотр этого ответа, потерпели неудачу. Та же самая проблема может быть замечена в оптимизированном ответе casperOne .
Для решения всех этих проблем вы можете использовать следующее:
Существует также ряд оптимизаций, которые вы могли бы ввести для итераций по порядку, выходящих за рамки порядка, который выходит за рамки данной области.
Какой метод вы должны выбрать? Это полностью зависит от проблемы, которую вы пытаетесь решить. Если вас не интересует первый недостаток, простой ответ невероятно привлекателен.
Обратите внимание, что, как и в большинстве методов, это не безопасно для многопоточности, вещи могут стать странными, если вы хотите сделать их поточно-ориентированными, вам нужно будет внести изменения
EnumeratorWrapper
.источник
Вы могли бы использовать несколько запросов, которые используют
Take
иSkip
, но я бы добавил слишком много итераций в исходный список.Скорее, я думаю, что вы должны создать свой собственный итератор, вот так:
Затем вы можете вызвать это и включить LINQ, чтобы вы могли выполнять другие операции с результирующими последовательностями.
В свете ответа Сэма я почувствовал, что есть более простой способ сделать это без:
Тем не менее, вот еще один проход, который я записал в методе расширения для
IEnumerable<T>
вызоваChunk
:Там нет ничего удивительного, просто базовая проверка ошибок.
Переходя к
ChunkInternal
:В основном, он получает
IEnumerator<T>
и вручную перебирает каждый элемент. Он проверяет, есть ли какие-либо элементы, которые должны быть перечислены в настоящее время. После перечисления каждого чанка, если не осталось ни одного элемента, он вспыхивает.Как только он обнаруживает, что в последовательности есть элементы, он делегирует ответственность за внутреннюю
IEnumerable<T>
реализациюChunkSequence
:Так как
MoveNext
он уже был вызван дляIEnumerator<T>
переданного вChunkSequence
, он возвращает элемент, возвращаемый,Current
а затем увеличивает счетчик, не заботясь о том, чтобы возвращать больше, чемchunkSize
элементы, и переходя к следующему элементу в последовательности после каждой итерации (но замыкается, если число количество полученных предметов превышает размер куска).Если элементов больше не осталось, то
InternalChunk
метод сделает еще один проход во внешнем цикле, но приMoveNext
вызове во второй раз он все равно вернет false, согласно документации (выделено мое):В этот момент цикл разорвется, и последовательность последовательностей прекратится.
Это простой тест:
Вывод:
Важное примечание: это не сработает, если вы не истощите всю дочернюю последовательность или не прервете ее в какой-либо точке родительской последовательности. Это важное предостережение, но если ваш вариант использования таков, что вы будете потреблять каждый элемент последовательности последовательностей, то это будет работать для вас.
Кроме того, он будет делать странные вещи, если вы играете с орденом, так же, как Сэм однажды .
источник
List<T>
, у вас, очевидно, будут проблемы с памятью из-за буферизации. Оглядываясь назад, я должен был отметить это в ответе, но в то время казалось, что основное внимание уделялось слишком многим итерациям. Тем не менее, ваше решение действительно волосатое. Я не проверял это, но теперь мне интересно, есть ли менее волосатое решение.Хорошо, вот мой взгляд на это:
Пример использования
Пояснения
Код работает путем вложения двух
yield
основанных итераторов.Внешний итератор должен отслеживать, сколько элементов было эффективно использовано внутренним (порционным) итератором. Это делается путем закрытия
remaining
сinnerMoveNext()
. Неиспользованные элементы чанка отбрасываются до того, как внешний итератор выдаст следующий чанк. Это необходимо, потому что в противном случае вы получите противоречивые результаты, когда внутренние перечисляемые значения не (полностью) потребляются (напримерc3.Count()
, возвращают 6).источник
полностью ленивый, не считая и не копируя:
источник
Я думаю, что следующее предложение будет самым быстрым. Я жертвую ленивостью источника Enumerable из-за возможности использовать Array.Copy и заранее знаю продолжительность каждого из моих подсписков.
источник
Мы можем улучшить решение @ JaredPar для ленивой оценки. Мы используем
GroupAdjacentBy
метод, который выдает группы последовательных элементов с одинаковым ключом:Поскольку группы приводятся один за другим, это решение эффективно работает с длинными или бесконечными последовательностями.
источник
Я написал метод расширения Clump несколько лет назад. Прекрасно работает, и это самая быстрая реализация здесь. :П
источник
System.Interactive предоставляет
Buffer()
для этой цели. Некоторое быстрое тестирование показывает, что производительность аналогична решению Сэма.источник
Buffer()
возвращается,IEnumerable<IList<T>>
так что да, у вас, вероятно, есть проблема - она не течет, как у вас.Вот подпрограмма разделения списка, которую я написал пару месяцев назад:
источник
Я нахожу, что этот маленький фрагмент хорошо справляется со своей работой.
источник
Что насчет этого?
Насколько я знаю, GetRange () является линейным с точки зрения количества взятых элементов. Так что это должно хорошо работать.
источник
Это старый вопрос, но это то, чем я закончил; он перечисляет перечисляемое только один раз, но создает списки для каждого из разделов. Он не страдает от неожиданного поведения при
ToArray()
вызове, как это делают некоторые реализации:источник
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int chunkSize)
Мы нашли, что решение Дэвида Б. сработало лучше всего. Но мы адаптировали его к более общему решению:
источник
Это следующее решение - самое компактное, которое я мог придумать, это O (n).
источник
Старый код, но это то, что я использовал:
источник
Если список имеет тип system.collections.generic, вы можете использовать доступный метод «CopyTo» для копирования элементов вашего массива в другие вложенные массивы. Вы указываете начальный элемент и количество элементов для копирования.
Вы также можете сделать 3 клона из своего исходного списка и использовать «RemoveRange» в каждом списке, чтобы уменьшить список до нужного размера.
Или просто создайте вспомогательный метод, который сделает это за вас.
источник
Это старое решение, но у меня был другой подход. Я использую,
Skip
чтобы перейти к желаемому смещению иTake
извлечь желаемое количество элементов:источник
Для всех, кто интересуется упакованным / поддерживаемым решением, библиотека MoreLINQ предоставляет
Batch
метод расширения, который соответствует вашему запрашиваемому поведению:Batch
Реализация похожа на ответ Камерона MacFarland в , с добавлением перегрузки для преобразования куска / пакет , прежде чем вернуться, и выполняет очень хорошо.источник
Использование модульного разбиения:
источник
Просто вставляю мои два цента. Если вы хотите «собрать» список (визуализировать слева направо), вы можете сделать следующее:
источник
Другой способ - использование оператора Rx Buffer.
источник
источник
Я взял основной ответ и сделал его контейнером IOC, чтобы определить, где разделить. ( Для тех, кто действительно хочет разделить только на 3 пункта, читая этот пост во время поиска ответа? )
Этот метод позволяет разделить на любой тип элемента по мере необходимости.
Так что для ОП код будет
источник
Такой перформативный, как подход Сэма Шафрана .
}
источник
Может работать с бесконечными генераторами:
Демо-код: https://ideone.com/GKmL7M
Но на самом деле я бы предпочел написать соответствующий метод без linq.
источник
Проверь это! У меня есть список элементов со счетчиком последовательности и датой. Каждый раз, когда последовательность перезапускается, я хочу создать новый список.
Ex. список сообщений.
Я хочу разделить список на отдельные списки, поскольку счетчик перезапускается. Вот код:
источник
Чтобы вставить мои два цента ...
Используя тип списка для источника, который будет разбит на части, я нашел другое очень компактное решение:
источник