I need to loop through an entire list of users, but need to grab 20 at a time.
foreach (var student in Class.Students.Take(20))
{
Console.WriteLine("You belong to Group " + groupNumber);
groupNumber++;
}
This way the first 20 will belong to Group 1, the second 20 to Group 2, and so on.
Is Take the correct syntax for th开发者_JS百科is? I believe Take will take 20 then be done. Thanks!
You can do something like this:
int i = 0;
foreach (var grouping in Class.Students.GroupBy(s => ++i / 20))
Console.WriteLine("You belong to Group " + grouping.Key.ToString());
For a similar problem I once made an extension method:
public static IEnumerable<IEnumerable<T>> ToChunks<T>(this IEnumerable<T> enumerable, int chunkSize)
{
int itemsReturned = 0;
var list = enumerable.ToList(); // Prevent multiple execution of IEnumerable.
int count = list.Count;
while (itemsReturned < count)
{
int currentChunkSize = Math.Min(chunkSize, count - itemsReturned);
yield return list.GetRange(itemsReturned, currentChunkSize);
itemsReturned += currentChunkSize;
}
}
Note that the last chunk may be smaller than the specified chunk size.
EDIT
The first version used Skip()/Take()
, but as Chris pointed out, GetRange
is considerably faster.
You could project the group number onto each item in the original list using Select
and an anonymous type like this:
var assigned = Class.Students.Select(
(item, index) => new
{
Student = item,
GroupNumber = index / 20 + 1
});
Then you can use it like this:
foreach (var item in assigned)
{
Console.WriteLine("Student = " + item.Student.Name);
Console.WriteLine("You belong to Group " + item.GroupNumber.ToString());
}
Or if you wanted to pick off a particular group.
foreach (var student in assigned.Where(x => x.GroupNumber == 5))
{
Console.WriteLine("Name = " + student.Name);
}
Or if you wanted to actually group them to perform aggregates
foreach (var group in assigned.GroupBy(x => x.GroupNumber))
{
Console.WriteLine("Average age = " + group.Select(x => x.Student.Age).Average().ToString());
}
Now if you just want to lump the items in batches and you do not care about having the batch number information you can create this simple extension method.
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> target, int size)
{
var assigned = target.Select(
(item, index) => new
{
Value = item,
BatchNumber = index / size
});
foreach (var group in assigned.GroupBy(x => x.BatchNumber))
{
yield return group.Select(x => x.Value);
}
}
And you could use your new extension method like this.
foreach (var batch in Class.Students.Batch(20))
{
foreach (var student in batch)
{
Console.WriteLine("Student = " + student.Name);
}
}
精彩评论