Saya mencoba untuk membagi daftar menjadi serangkaian daftar yang lebih kecil.
Masalah Saya: Fungsi saya untuk membagi daftar tidak membaginya menjadi daftar dengan ukuran yang benar. Seharusnya membaginya menjadi daftar ukuran 30 tetapi sebaliknya membaginya menjadi daftar ukuran 114?
Bagaimana saya bisa membuat fungsi saya membagi daftar menjadi X jumlah Daftar ukuran 30 atau kurang ?
public static List<List<float[]>> splitList(List <float[]> locations, int nSize=30)
{
List<List<float[]>> list = new List<List<float[]>>();
for (int i=(int)(Math.Ceiling((decimal)(locations.Count/nSize))); i>=0; i--) {
List <float[]> subLocat = new List <float[]>(locations);
if (subLocat.Count >= ((i*nSize)+nSize))
subLocat.RemoveRange(i*nSize, nSize);
else subLocat.RemoveRange(i*nSize, subLocat.Count-(i*nSize));
Debug.Log ("Index: "+i.ToString()+", Size: "+subLocat.Count.ToString());
list.Add (subLocat);
}
return list;
}
Jika saya menggunakan fungsi pada daftar ukuran 144 maka hasilnya adalah:
Indeks: 4, Ukuran: 120
Indeks: 3, Ukuran: 114
Indeks: 2, Ukuran: 114
Indeks: 1, Ukuran: 114
Indeks: 0, Ukuran: 114
Jawaban:
Versi generik:
sumber
GetRange(3, 3)
Saya akan menyarankan untuk menggunakan metode ekstensi ini untuk memotong daftar sumber ke sub-daftar dengan ukuran potongan yang ditentukan:
Misalnya, jika Anda memotong daftar 18 item dengan 5 item per potong, itu memberi Anda daftar 4 sub-daftar dengan item berikut di dalam: 5-5-5-3.
sumber
ToList()
s, dan biarkan evaluasi malas melakukan keajaiban itu.bagaimana tentang:
sumber
ToList
tetapi saya tidak akan repot-repot mencoba mengoptimalkannya - itu sangat sepele dan tidak mungkin merupakan hambatan. Keuntungan utama dari implementasi ini adalah hal-hal sepele yang mudah dipahami. Jika mau, Anda dapat menggunakan jawaban yang diterima itu tidak membuat daftar itu tetapi sedikit lebih rumit..Skip(n)
iterates atasn
elemen setiap kali dipanggil, sementara ini mungkin ok, penting untuk mempertimbangkan kode kinerja-kritis. stackoverflow.com/questions/20002975/….Skip()
di basis kode perusahaan saya, dan meskipun mereka mungkin tidak "optimal" mereka bekerja dengan baik. Hal-hal seperti operasi DB membutuhkan waktu lebih lama. Tapi saya pikir ini hal yang penting untuk dicatat bahwa.Skip()
"menyentuh" setiap elemen <n di jalan bukannya langsung ke elemen-n secara langsung (seperti yang Anda harapkan). Jika iterator Anda memiliki efek samping dari menyentuh elemen.Skip()
dapat menjadi penyebab bug yang sulit ditemukan.Solusi Serj-Tm baik-baik saja, juga ini adalah versi generik sebagai metode ekstensi untuk daftar (memasukkannya ke dalam kelas statis):
sumber
Saya menemukan jawaban yang diterima (Serj-Tm) paling kuat, tetapi saya ingin menyarankan versi generik.
sumber
Perpustakaan MoreLinq memiliki metode yang disebut
Batch
Hasilnya adalah
ids
dibagi menjadi 5 bongkahan dengan 2 elemen.sumber
Saya memiliki metode generik yang akan mengambil semua jenis termasuk float, dan telah diuji unit, semoga membantu:
sumber
values.Count()
akan menyebabkan pencacahan penuh dan kemudianvalues.ToList()
lainnya. Lebih aman untuk melakukannyavalues = values.ToList()
sudah terwujud.Sementara banyak jawaban di atas melakukan pekerjaan, semuanya gagal total pada urutan yang tidak pernah berakhir (atau urutan yang sangat panjang). Berikut ini adalah implementasi sepenuhnya on-line yang menjamin kompleksitas waktu dan memori terbaik. Kami hanya mengulangi sumber yang disebutkan sekali saja dan menggunakan pengembalian hasil untuk evaluasi malas. Konsumen dapat membuang daftar pada setiap iterasi yang membuat jejak memori sama dengan daftar dengan
batchSize
sejumlah elemen.EDIT: Baru saja menyadari OP bertanya tentang memecah
List<T>
menjadi lebih kecilList<T>
, jadi komentar saya tentang enumerables tak terbatas tidak berlaku untuk OP, tetapi dapat membantu orang lain yang berakhir di sini. Komentar-komentar ini sebagai tanggapan terhadap solusi diposting lainnya yang memang digunakanIEnumerable<T>
sebagai input untuk fungsi mereka, namun menghitung sumber yang dapat dihitung berulang kali.sumber
IEnumerable<IEnumerable<T>>
versinya lebih baik karena tidak melibatkan banyakList
konstruksi.IEnumerable<IEnumerable<T>>
adalah bahwa implementasi cenderung bergantung pada konsumen yang secara penuh menghitung setiap bagian yang dapat dihitung. Saya yakin solusi dapat diutarakan dengan cara untuk menghindari masalah itu, tapi saya pikir kode yang dihasilkan bisa menjadi sangat cepat. Juga, karena malas, kami hanya membuat satu daftar pada satu waktu dan alokasi memori terjadi tepat satu kali per daftar karena kami tahu ukuran di muka.Tambahan setelah komentar yang sangat berguna dari mhand di akhir
Jawaban asli
Meskipun sebagian besar solusi mungkin berhasil, saya pikir mereka tidak terlalu efisien. Misalkan jika Anda hanya menginginkan beberapa item pertama dari beberapa chunks pertama. Maka Anda tidak ingin mengulangi semua (miliaran) item dalam urutan Anda.
Berikut ini akan paling banyak menyebutkan dua kali: sekali untuk Ambil dan sekali untuk Lewati. Itu tidak akan menghitung lebih dari elemen daripada yang akan Anda gunakan:
Berapa kali ini akan menghitung urutan?
Misalkan Anda membagi sumber Anda menjadi beberapa bagian
chunkSize
. Anda hanya menghitung potongan N pertama. Dari setiap potongan yang disebutkan, Anda hanya akan menghitung elemen M pertama.Any akan mendapatkan Enumerator, lakukan 1 MoveNext () dan kembalikan nilai yang dikembalikan setelah Membuang Enumerator. Ini akan dilakukan N kali
Menurut sumber referensi ini akan melakukan sesuatu seperti:
Ini tidak banyak membantu sampai Anda mulai menghitung lebih dari Chunk yang diambil. Jika Anda mengambil beberapa bongkahan, tetapi memutuskan untuk tidak menghitung lebih dari bongkahan pertama, foreach tidak dieksekusi, karena debugger Anda akan menunjukkan kepada Anda.
Jika Anda memutuskan untuk mengambil elemen M pertama dari chunk pertama maka pengembalian hasil dieksekusi tepat M kali. Ini berarti:
Setelah potongan pertama telah dikembalikan, kami lewati Potongan pertama ini:
Sekali lagi: kita akan melihat sumber referensi untuk menemukan
skipiterator
Seperti yang Anda lihat,
SkipIterator
panggilanMoveNext()
satu kali untuk setiap elemen di dalam Chunk. Itu tidak meneleponCurrent
.Jadi per Chunk kita melihat bahwa berikut ini dilakukan:
Mengambil():
Jika konten disebutkan: GetEnumerator (), satu MoveNext dan satu Current per item yang disebutkan, Buang enumerator;
Lewati (): untuk setiap chunk yang disebutkan (BUKAN isi chunk): GetEnumerator (), MoveNext () chunkUkuran kali, tanpa arus! Buang enumerator
Jika Anda melihat apa yang terjadi dengan enumerator, Anda akan melihat bahwa ada banyak panggilan ke MoveNext (), dan hanya panggilan ke
Current
untuk item TSource yang Anda putuskan untuk akses.Jika Anda mengambil N Potongan ukuran chunkSize, maka panggilan ke MoveNext ()
Jika Anda memutuskan untuk menghitung hanya elemen M pertama dari setiap chunk yang diambil, maka Anda perlu memanggil MoveNext M kali per Chunk yang disebutkan.
Jumlah seluruhnya
Jadi, jika Anda memutuskan untuk menghitung semua elemen dari semua bongkahan:
Apakah MoveNext banyak pekerjaan atau tidak, tergantung pada jenis urutan sumber. Untuk daftar dan array, ini adalah kenaikan indeks sederhana, dengan mungkin pemeriksaan di luar rentang.
Tetapi jika IEnumerable Anda adalah hasil dari query database, pastikan bahwa data benar-benar terwujud di komputer Anda, jika tidak, data akan diambil beberapa kali. DbContext dan Dapper akan mentransfer data dengan benar ke proses lokal sebelum dapat diakses. Jika Anda menyebutkan urutan yang sama beberapa kali, itu tidak diambil beberapa kali. Dapper mengembalikan objek yang Daftar, DbContext mengingat bahwa data sudah diambil.
Tergantung pada Repositori Anda apakah bijaksana untuk memanggil AsEnumerable () atau ToLists () sebelum Anda mulai membagi item dalam Potongan
sumber
2*chunkSize
? Ini mematikan tergantung pada sumber yang dapat dihitung (mungkin DB didukung, atau sumber non-memoized lainnya). Bayangkan enumerable ini sebagai inputEnumerable.Range(0, 10000).Select(i => DateTime.UtcNow)
- Anda akan mendapatkan waktu yang berbeda setiap kali Anda menghitung enumerable karena itu tidak di-memoEnumerable.Range(0, 10).Select(i => DateTime.UtcNow)
. Dengan memohonAny
Anda akan menghitung ulang waktu saat ini setiap kali. Tidak terlalu buruk untukDateTime.UtcNow
, tetapi pertimbangkan enumerable yang didukung oleh koneksi database / sql cursor atau yang serupa. Saya telah melihat kasus-kasus di mana ribuan panggilan DB dikeluarkan karena pengembang tidak memahami kemungkinan dampak 'enumerasi ganda yang dapat dihitung' - ReSharper juga memberikan petunjuk untuk ini jugasumber
sumber
Bagaimana dengan yang ini? Idenya adalah menggunakan hanya satu loop. Dan, siapa tahu, mungkin Anda hanya menggunakan implementasi IList melalui kode Anda dan Anda tidak ingin memasukkan ke Daftar.
sumber
Satu lagi
sumber
sumber
sumber
Saya telah menemui kebutuhan yang sama ini, dan saya menggunakan kombinasi metode Lewati () dan Ambil () . Saya mengalikan jumlah yang saya ambil dengan jumlah iterasi sejauh ini, dan itu memberi saya jumlah item untuk dilewati, kemudian saya mengambil grup berikutnya.
sumber
Berdasarkan Dimitry Pavlov jawab saya akan menghapus
.ToList()
. Dan juga menghindari kelas anonim. Sebaliknya saya suka menggunakan struct yang tidak memerlukan alokasi memori tumpukan. (AValueTuple
juga akan melakukan pekerjaan.)Ini dapat digunakan seperti berikut ini yang hanya mengulang koleksi sekali dan juga tidak mengalokasikan memori signifikan.
Jika daftar konkret benar-benar diperlukan maka saya akan melakukannya seperti ini:
sumber