Apakah ada cara saya bisa memisahkan List<SomeObject>
beberapa daftar yang terpisah SomeObject
, menggunakan indeks item sebagai pembatas setiap split?
Izinkan saya memberi contoh:
Saya memiliki List<SomeObject>
dan saya memerlukan List<List<SomeObject>>
atau List<SomeObject>[]
, sehingga masing-masing daftar yang dihasilkan ini akan berisi sekelompok 3 item dari daftar asli (berurutan).
misalnya.:
Daftar Asli:
[a, g, e, w, p, s, q, f, x, y, i, m, c]
Daftar hasil:
[a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]
Saya juga membutuhkan ukuran daftar yang dihasilkan untuk menjadi parameter dari fungsi ini.
sumber
[a,g,e]
sebelum menghitung lagi daftar asli.GroupBy(x=>f(x)).First()
tidak akan pernah menghasilkan grup. OP bertanya tentang daftar, tetapi jika kami menulis untuk bekerja dengan IEnumerable, hanya membuat satu iterasi, kami menuai keuntungan kinerja.Pertanyaan ini agak lama, tetapi saya baru saja menulis ini, dan saya pikir ini sedikit lebih elegan daripada solusi yang diusulkan lainnya:
sumber
if (chunksize <= 0) throw new ArgumentException("Chunk size must be greater than zero.", "chunksize");
O(n²)
. Anda dapat mengulangi daftar dan mendapatkanO(n)
waktu.source
diganti dengan yang dibungkusIEnumerable
setiap kali. Jadi mengambil elemen darisource
lapisanSkip
sSecara umum pendekatan yang disarankan oleh CaseyB berfungsi dengan baik, bahkan jika Anda mengirimkannya
List<T>
sulit untuk menyalahkannya, mungkin saya akan mengubahnya menjadi:Yang akan menghindari rantai panggilan besar-besaran. Meskipun demikian, pendekatan ini memiliki kelemahan umum. Ini mematerialisasikan dua enumerasi per potong, untuk menyoroti masalah coba jalankan:
Untuk mengatasinya, kita dapat mencoba pendekatan Cameron , yang lulus tes di atas dalam warna terbang karena hanya berjalan satu kali pencacahan.
Masalahnya adalah ia memiliki cacat yang berbeda, itu mematerialisasi setiap item di setiap chunk, masalah dengan pendekatan itu adalah Anda kehabisan memori.
Untuk mengilustrasikannya, coba jalankan:
Akhirnya, setiap implementasi harus dapat menangani keluarnya urutan potongan, misalnya:
Banyak solusi yang sangat optimal seperti revisi pertama saya untuk jawaban ini gagal di sana. Masalah yang sama dapat dilihat pada jawaban optimal casperOne .
Untuk mengatasi semua masalah ini, Anda dapat menggunakan yang berikut:
Ada juga serangkaian optimisasi yang dapat Anda perkenalkan untuk iterasi chunk yang out-of-order, yang berada di luar cakupan di sini.
Metode apa yang harus Anda pilih? Ini benar-benar tergantung pada masalah yang Anda coba selesaikan. Jika Anda tidak peduli dengan cacat pertama, jawaban sederhana sangat menarik.
Catatan seperti pada kebanyakan metode, ini tidak aman untuk multi threading, hal-hal bisa menjadi aneh jika Anda ingin menjadikannya aman, Anda harus mengubahnya
EnumeratorWrapper
.sumber
Anda bisa menggunakan sejumlah pertanyaan yang menggunakan
Take
danSkip
, tetapi itu akan menambah terlalu banyak iterasi pada daftar asli, saya percaya.Sebaliknya, saya pikir Anda harus membuat iterator sendiri, seperti:
Anda kemudian dapat memanggil ini dan itu diaktifkan LINQ sehingga Anda dapat melakukan operasi lain pada urutan yang dihasilkan.
Mengingat jawaban Sam , saya merasa ada cara yang lebih mudah untuk melakukan ini tanpa:
Yang mengatakan, inilah pass lain, yang telah saya kodifikasikan dalam metode ekstensi untuk
IEnumerable<T>
dipanggilChunk
:Tidak ada yang mengejutkan di sana, hanya pengecekan kesalahan dasar.
Pindah ke
ChunkInternal
:Pada dasarnya, ia mendapat
IEnumerator<T>
dan secara manual beralih melalui setiap item. Ia memeriksa untuk melihat apakah ada item yang saat ini akan disebutkan. Setelah setiap potongan dihitung melalui, jika tidak ada barang yang tersisa, itu pecah.Setelah mendeteksi ada item dalam urutan, itu mendelegasikan tanggung jawab untuk
IEnumerable<T>
implementasi batin untukChunkSequence
:Karena
MoveNext
sudah dipanggil pada yangIEnumerator<T>
diteruskan keChunkSequence
, itu menghasilkan item yang dikembalikan olehCurrent
dan kemudian menambah hitungan, memastikan tidak pernah kembali lebih darichunkSize
item dan pindah ke item berikutnya dalam urutan setelah setiap iterasi (tetapi hubung pendek jika jumlah item yang dihasilkan melebihi ukuran chunk).Jika tidak ada item yang tersisa, maka
InternalChunk
metode akan membuat pass lain di loop luar, tetapi ketikaMoveNext
dipanggil untuk kedua kalinya, itu akan tetap kembali salah, sesuai dokumentasi (penekanan tambang):Pada titik ini, loop akan terputus, dan urutan urutan akan berakhir.
Ini adalah tes sederhana:
Keluaran:
Catatan penting, ini tidak akan berfungsi jika Anda tidak menguras seluruh urutan anak atau mematahkan pada titik mana pun dalam urutan induk. Ini adalah peringatan penting, tetapi jika kasus penggunaan Anda adalah bahwa Anda akan mengkonsumsi setiap elemen dari urutan sekuens, maka ini akan bekerja untuk Anda.
Selain itu, itu akan melakukan hal-hal aneh jika Anda bermain dengan pesanan, seperti yang dilakukan Sam pada satu titik .
sumber
List<T>
, Anda jelas akan memiliki masalah memori karena buffering. Dalam retrospeksi, saya seharusnya mencatat itu dalam jawabannya, tetapi tampaknya pada saat itu fokusnya adalah pada terlalu banyak iterasi. Yang mengatakan, solusi Anda memang lebih hairier. Saya belum mengujinya, tetapi sekarang saya bertanya-tanya apakah ada solusi yang kurang berbulu.Oke, ini pendapat saya:
Contoh Penggunaan
Penjelasan
Kode berfungsi dengan menyarangkan dua
yield
iterator berbasis.Iterator luar harus melacak berapa banyak elemen yang telah dikonsumsi secara efektif oleh iterator bagian dalam (chunk). Ini dilakukan dengan menutup
remaining
denganinnerMoveNext()
. Unsur-unsur potongan yang tidak dikonsumsi dibuang sebelum potongan berikutnya dihasilkan oleh iterator luar. Ini diperlukan karena jika tidak, Anda mendapatkan hasil yang tidak konsisten, ketika enumerables bagian dalam tidak (sepenuhnya) dikonsumsi (misalnyac3.Count()
akan mengembalikan 6).sumber
benar-benar malas, tidak ada penghitungan atau penyalinan:
sumber
Saya pikir saran berikut akan menjadi yang tercepat. Saya mengorbankan kemalasan sumber yang tak terhitung jumlahnya untuk kemampuan menggunakan Array. Salin dan ketahui sebelumnya berapa lama masing-masing sublists saya.
sumber
Kami dapat meningkatkan solusi @ JaredPar untuk melakukan evaluasi malas yang sebenarnya. Kami menggunakan a
GroupAdjacentBy
metode yang menghasilkan grup elemen berurutan dengan kunci yang sama:Karena grup dihasilkan satu-per-satu, solusi ini bekerja secara efisien dengan urutan panjang atau tak terbatas.
sumber
Saya menulis metode ekstensi Clump beberapa tahun yang lalu. Bekerja dengan baik, dan merupakan implementasi tercepat di sini. : P
sumber
System.Interactive menyediakan
Buffer()
untuk tujuan ini. Beberapa pengujian cepat menunjukkan kinerja mirip dengan solusi Sam.sumber
Buffer()
kembaliIEnumerable<IList<T>>
jadi ya, Anda mungkin memiliki masalah di sana - tidak mengalir seperti milik Anda.Berikut adalah daftar rutin yang saya tulis beberapa bulan yang lalu:
sumber
Saya menemukan potongan kecil ini melakukan pekerjaan dengan cukup baik.
sumber
Bagaimana dengan yang ini?
Sejauh yang saya tahu, GetRange () adalah linier dalam hal jumlah item yang diambil. Jadi ini harus berkinerja baik.
sumber
Ini adalah pertanyaan lama tetapi inilah yang akhirnya saya dapatkan; itu menghitung enumerable hanya sekali, tetapi membuat daftar untuk masing-masing partisi. Itu tidak menderita dari perilaku tak terduga ketika
ToArray()
dipanggil seperti beberapa implementasi lakukan:sumber
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int chunkSize)
Kami menemukan solusi David B bekerja paling baik. Tetapi kami mengadaptasinya ke solusi yang lebih umum:
sumber
Solusi berikut ini adalah yang paling ringkas yang bisa saya buat yaitu O (n).
sumber
Kode lama, tapi ini yang saya gunakan:
sumber
Jika daftar ini bertipe system.collections.generic Anda dapat menggunakan metode "CopyTo" yang tersedia untuk menyalin elemen array Anda ke sub array lainnya. Anda menentukan elemen awal dan jumlah elemen yang akan disalin.
Anda juga dapat membuat 3 klon dari daftar asli Anda dan menggunakan "RemoveRange" pada setiap daftar untuk mengecilkan daftar ke ukuran yang Anda inginkan.
Atau buat metode pembantu untuk melakukannya untuk Anda.
sumber
Ini solusi lama tapi saya punya pendekatan berbeda. Saya gunakan
Skip
untuk pindah ke offset yang diinginkan danTake
untuk mengekstrak jumlah elemen yang diinginkan:sumber
Bagi siapa pun yang tertarik dengan solusi yang dipaket / dipelihara, perpustakaan MoreLINQ menyediakan
Batch
metode ekstensi yang cocok dengan perilaku yang Anda minta:The
Batch
pelaksanaan mirip dengan jawaban Cameron MacFarland ini , dengan penambahan kelebihan beban untuk mengubah potongan / batch sebelum kembali, dan melakukan cukup baik.sumber
Menggunakan partisi modular:
sumber
Hanya memasukkan dua sen saya. Jika Anda ingin "menyatukan" daftar (visualisasikan dari kiri ke kanan), Anda dapat melakukan hal berikut:
sumber
Cara lain menggunakan operator Rx Buffer
sumber
sumber
Saya mengambil jawaban utama dan membuatnya menjadi wadah IOC untuk menentukan di mana harus dibagi. ( Untuk siapa yang benar-benar hanya ingin membagi 3 item, dalam membaca posting ini sambil mencari jawaban? )
Metode ini memungkinkan seseorang untuk membagi pada setiap jenis item sesuai kebutuhan.
Jadi untuk OP kodenya
sumber
Begitu performatik seperti pendekatan Sam Saffron .
}
sumber
Dapat bekerja dengan generator yang tak terbatas:
Kode demo: https://ideone.com/GKmL7M
Tapi sebenarnya saya lebih suka menulis metode yang sesuai tanpa LINQ.
sumber
Lihat ini! Saya memiliki daftar elemen dengan penghitung urutan dan tanggal. Untuk setiap kali urutan dimulai ulang, saya ingin membuat daftar baru.
Ex. daftar pesan.
Saya ingin membagi daftar menjadi daftar terpisah ketika penghitung dimulai ulang. Ini kodenya:
sumber
Untuk memasukkan dua sen saya ...
Dengan menggunakan tipe daftar untuk sumber yang akan dipotong, saya menemukan solusi yang sangat kompak:
sumber