Anda bisa menggunakan sejumlah pertanyaan yang menggunakan Take
dan Skip
, tetapi itu akan menambah terlalu banyak iterasi pada daftar asli, saya percaya.
Sebaliknya, saya pikir Anda harus membuat iterator sendiri, seperti:
public static IEnumerable<IEnumerable<T>> GetEnumerableOfEnumerables<T>(
IEnumerable<T> enumerable, int groupSize)
{
// The list to return.
List<T> list = new List<T>(groupSize);
// Cycle through all of the items.
foreach (T item in enumerable)
{
// Add the item.
list.Add(item);
// If the list has the number of elements, return that.
if (list.Count == groupSize)
{
// Return the list.
yield return list;
// Set the list to a new list.
list = new List<T>(groupSize);
}
}
// Return the remainder if there is any,
if (list.Count != 0)
{
// Return the list.
yield return list;
}
}
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:
- Mengulangi daftar lagi (yang awalnya tidak saya lakukan)
- Mewujudkan item dalam kelompok sebelum melepaskan bongkahan (untuk bongkahan besar item, akan ada masalah memori)
- Semua kode yang diposting Sam
Yang mengatakan, inilah pass lain, yang telah saya kodifikasikan dalam metode ekstensi untuk IEnumerable<T>
dipanggilChunk
:
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source,
int chunkSize)
{
// Validate parameters.
if (source == null) throw new ArgumentNullException("source");
if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize",
"The chunkSize parameter must be a positive value.");
// Call the internal implementation.
return source.ChunkInternal(chunkSize);
}
Tidak ada yang mengejutkan di sana, hanya pengecekan kesalahan dasar.
Pindah ke ChunkInternal
:
private static IEnumerable<IEnumerable<T>> ChunkInternal<T>(
this IEnumerable<T> source, int chunkSize)
{
// Validate parameters.
Debug.Assert(source != null);
Debug.Assert(chunkSize > 0);
// Get the enumerator. Dispose of when done.
using (IEnumerator<T> enumerator = source.GetEnumerator())
do
{
// Move to the next element. If there's nothing left
// then get out.
if (!enumerator.MoveNext()) yield break;
// Return the chunked sequence.
yield return ChunkSequence(enumerator, chunkSize);
} while (true);
}
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 untuk ChunkSequence
:
private static IEnumerable<T> ChunkSequence<T>(IEnumerator<T> enumerator,
int chunkSize)
{
// Validate parameters.
Debug.Assert(enumerator != null);
Debug.Assert(chunkSize > 0);
// The count.
int count = 0;
// There is at least one item. Yield and then continue.
do
{
// Yield the item.
yield return enumerator.Current;
} while (++count < chunkSize && enumerator.MoveNext());
}
Karena MoveNext
sudah dipanggil pada yang IEnumerator<T>
diteruskan ke ChunkSequence
, itu menghasilkan item yang dikembalikan oleh Current
dan kemudian menambah hitungan, memastikan tidak pernah kembali lebih dari chunkSize
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 ketika MoveNext
dipanggil untuk kedua kalinya, itu akan tetap kembali salah, sesuai dokumentasi (penekanan tambang):
Jika MoveNext melewati akhir koleksi, enumerator diposisikan setelah elemen terakhir dalam koleksi dan MoveNext mengembalikan false. Ketika enumerator berada pada posisi ini, panggilan berikutnya ke MoveNext juga mengembalikan false sampai Reset dipanggil.
Pada titik ini, loop akan terputus, dan urutan urutan akan berakhir.
Ini adalah tes sederhana:
static void Main()
{
string s = "agewpsqfxyimc";
int count = 0;
// Group by three.
foreach (IEnumerable<char> g in s.Chunk(3))
{
// Print out the group.
Console.Write("Group: {0} - ", ++count);
// Print the items.
foreach (char c in g)
{
// Print the item.
Console.Write(c + ", ");
}
// Finish the line.
Console.WriteLine();
}
}
Keluaran:
Group: 1 - a, g, e,
Group: 2 - w, p, s,
Group: 3 - q, f, x,
Group: 4 - y, i, m,
Group: 5 - c,
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 .