คุณสามารถใช้ข้อความค้นหาจำนวนมากที่ใช้Take
และSkip
แต่เชื่อว่าจะเพิ่มการทำซ้ำหลายรายการในรายการเดิมฉันเชื่อ
ค่อนข้างฉันคิดว่าคุณควรสร้างตัววนซ้ำของคุณเองเช่น:
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;
}
}
จากนั้นคุณสามารถเรียกสิ่งนี้ได้และมันเปิดใช้งาน LINQ เพื่อให้คุณสามารถดำเนินการอื่น ๆ ตามลำดับที่ได้
ในแง่ของคำตอบของแซมผมรู้สึกว่ามีวิธีที่ง่ายต่อการทำเช่นนี้โดย:
- วนซ้ำในรายการอีกครั้ง (ซึ่งฉันไม่ได้ทำในตอนแรก)
- การทำให้รายการเป็นกลุ่มเป็นจริงก่อนปล่อยก้อน (สำหรับชิ้นส่วนขนาดใหญ่ของรายการจะมีปัญหาหน่วยความจำ)
- รหัสทั้งหมดที่แซมโพสต์
ที่กล่าวว่านี่คืออีกหนึ่งผ่านซึ่งฉันได้ประมวลในวิธีการขยายไป IEnumerable<T>
เรียกว่าChunk
:
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);
}
ไม่มีอะไรน่าแปลกใจที่นั่นเพียงตรวจสอบข้อผิดพลาดพื้นฐาน
ย้ายไปที่ 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);
}
โดยทั่วไปจะได้รับ IEnumerator<T>
และทำซ้ำด้วยตนเองผ่านแต่ละรายการ มันจะตรวจสอบเพื่อดูว่ามีรายการใด ๆ ที่จะระบุในปัจจุบัน หลังจากแต่ละอันถูกแจกแจงผ่านหากไม่มีรายการใดเหลืออยู่มันจะแบ่งออก
เมื่อตรวจพบว่ามีรายการอยู่ในลำดับมันจะมอบหมายความรับผิดชอบสำหรับIEnumerable<T>
การนำไปใช้ภายใน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());
}
เนื่องจากMoveNext
มีการเรียกเมื่อIEnumerator<T>
ผ่านไปChunkSequence
แล้วจึงให้รายการที่ส่งคืนโดยCurrent
แล้วเพิ่มจำนวนการตรวจสอบให้แน่ใจว่าจะไม่ส่งคืนมากกว่าchunkSize
รายการและย้ายไปยังรายการถัดไปในลำดับหลังจากการวนซ้ำทุกครั้ง (แต่ลัดวงจรถ้าจำนวน รายการที่ให้ผลลัพธ์มีขนาดเกินขนาด)
หากไม่มีรายการที่เหลืออยู่InternalChunk
วิธีการจะทำการส่งผ่านอีกครั้งในลูปด้านนอก แต่เมื่อMoveNext
เรียกว่าครั้งที่สองมันจะยังคงกลับเท็จตามเอกสาร (เน้นการระเบิด):
ถ้า MoveNext ผ่านจุดสิ้นสุดของคอลเลกชันตัวแจงนับจะถูกวางตำแหน่งหลังจากองค์ประกอบสุดท้ายในคอลเลกชันและ MoveNext จะส่งกลับค่าเท็จ เมื่อตัวแจงนับอยู่ที่ตำแหน่งนี้การเรียกใช้ MoveNext ที่ตามมาจะส่งกลับค่าเท็จจนกว่าจะมีการเรียกใช้การตั้งค่าใหม่
ณ จุดนี้ลูปจะแตกและลำดับของลำดับจะสิ้นสุดลง
นี่คือการทดสอบอย่างง่าย:
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();
}
}
เอาท์พุท:
Group: 1 - a, g, e,
Group: 2 - w, p, s,
Group: 3 - q, f, x,
Group: 4 - y, i, m,
Group: 5 - c,
หมายเหตุสำคัญสิ่งนี้จะไม่ทำงานหากคุณไม่ระบายลำดับลูกทั้งหมดหรือแตกที่จุดใด ๆ ในลำดับหลัก นี่เป็นข้อแม้ที่สำคัญ แต่ถ้ากรณีการใช้ของคุณคือคุณจะใช้องค์ประกอบทุกอย่างในลำดับของลำดับแล้วสิ่งนี้จะได้ผลสำหรับคุณ
นอกจากนี้ก็จะทำสิ่งแปลกถ้าคุณเล่นกับการสั่งซื้อเช่นเดียวกับแซมได้เมื่อถึงจุดหนึ่ง