Reputation: 99
I would like to know how to group item and split every N record by using LINQ
# |ITEM |QUANTITY
==================
1 |ITEM01| 10
2 |ITEM01| 10
3 |ITEM01| 10
. . .
. . .
22|ITEM01| 10
23|ITEM02| 50
24|ITEM02| 50
Suppose there's a list with 23 ITEM001 and 2 ITEM002
How to get
ITEM |QUANTITY
=================
ITEM001 | 100
ITEM001 | 100
ITEM001 | 20
ITEM002 | 100
Group by ITEM, if grouped > 10, go to next
Is there any way to achieve it? Thanks for you help!
Thanks for those nice guys help! Further question, now i would like to group the list like (grouped every 10 records, after grouping, if count does not reach 10, do not group), Sorry for my poor English :(
ITEM |QUANTITY
=================
ITEM01 | 100
ITEM01 | 100
ITEM01 | 10
ITEM01 | 10
ITEM01 | 10
ITEM02 | 50
ITEM02 | 50
Thanks for your help again!
Upvotes: 3
Views: 3996
Reputation: 16878
Note: Below answer groups items by sum, not count. Nevertheless, I leave it here for future reference. Maybe someone will ever have similar problem.
You can achieve it with the following LINQ query:
List<Item> items = new List<Item>()
{
new Item() { Name = "Item01", Quantity = 40 }, // 130
new Item() { Name = "Item01", Quantity = 70 },
new Item() { Name = "Item01", Quantity = 10 },
new Item() { Name = "Item01", Quantity = 10 },
new Item() { Name = "Item02", Quantity = 50 }, // 100
new Item() { Name = "Item02", Quantity = 50 },
new Item() { Name = "Item03", Quantity = 10 } // 10
};
var result =
// Group by Name, calculate total sum for each group
items.GroupBy(i => i.Name, (k, v) => new Item()
{
Name = k,
Quantity = v.Sum(i => i.Quantity)
})
// Split groups into 100 packages
.SelectMany(i => Enumerable.Range(0, (int)Math.Ceiling(i.Quantity / 100.0))
.Select(n => new Item()
{
Name = i.Name,
Quantity = i.Quantity > ((n + 1) * 100) ? 100 : i.Quantity - n * 100
}))
.ToList();
Then you have four elements in the list:
Item01 100
Item01 30
Item02 100
Item03 10
Upvotes: 0
Reputation: 236238
var query =
items.GroupBy(item => item.Name)
.SelectMany(g => g.Select((item, index) => new { item, index })
.GroupBy(x => x.index / 10)
.Select(batch => new Item {
Name = batch.First().item.Name,
Quantity = batch.Sum(x => x.item.Quantity)
})).OrderBy(item => item.Name);
This query can be simplified if you will use MoreLINQ (available from NuGet) Batch extension or write your own one:
var query =
items.GroupBy(item => item.Name)
.SelectMany(g => g.Batch(10)
.Select(batch => new Item {
Name = batch.First().Name,
Quantity = batch.Sum(item => item.Quantity)
})).OrderBy(item => item.Name);
Assume you have item class like this
public class Item
{
public string Name { get; set; }
public int Quantity { get; set; }
}
Then this sample list of items (constructed with NBuilder):
var items = Builder<Item>.CreateListOfSize(32)
.TheFirst(23)
.With(i => i.Name = "ITEM01")
.And(i => i.Quantity = 10)
.TheNext(9)
.With(i => i.Name = "ITEM02")
.And(i => i.Quantity = 50)
.Build();
Will give result:
[
{ Name: "ITEM01", Quantity: 100 },
{ Name: "ITEM01", Quantity: 100 },
{ Name: "ITEM01", Quantity: 30 },
{ Name: "ITEM02", Quantity: 450 }
]
NOTE: @Thejaka solution will give you five items in this case - there will be two ITEM02 items with quantity 350 and 100.
Upvotes: 4