I need to retrieve an average TimeSpan from a List of grouped objects, but have no idea where to start.
Each object in the list has a property of type TimeSpan and I've grouped the List by another property. Now I need the average TimeSpan from each group.
List<Item> Items = new List<Item>();
Item _item1 = new Item { Category = "A", Duration = _someTimeSpan1};
Item _item2 = new Item { Category = "B", Duration = _someTimeSpan2};
Item _item3 = new Item { Category = "A", Duration = _someTimeSpan3};
Items.Add(_item1);
Items.Add(_item2);
Items.Add(_item3);
var _groupedItems = Items.GroupBy(i => i.Category);
In the above example _item1 and _item3 are grouped on Category and I need the average of Duration for those two items that are in the group.
Any help is appreciated.
foreach (var group in _groupedItems) {
var avg = TimeSpan.FromSeconds(group.Average(i => i.Duration.TotalSeconds));
}
var _groupedItems = Items.GroupBy(i => i.Category)
.Select(g => new { Cat = g.Key,
Avg = new TimeSpan(Convert.ToInt64(g.Select(x=>x.Duration.Ticks).Average())) });
That should do the trick:
foreach (var group in _groupedItems){
Console.WriteLine(group.Average(g => g.Duration.TotalMilliseconds));
}
Related
I have a List<Returns> as below
Item ManCode ShippedQty
ITM01 A10 1
ITM02 A11 2
ITM01 A10 3
Here the first and 3 rd rows have same values for Item and Mancode. in this case, the both the items should be merged and shippedQty values must be added to 4.
There should be only 2 items in the final list
You can try to use linq GroupBy with Sum function.
list.GroupBy(x=> new{x.Item ,x.ManCode}).Select(
x=> new {
Item = x.key.Item,
ManCode = x.key.ManCode,
ShippedQty = x.Sum(y=>y.ShippedQty)
}
);
var result = items.GroupBy(x => new { x.Item, x.ManCode })
.Select(x => new Returns
{
Item = x.Key.Item,
ManCode = x.Key.ManCode,
ShippedQty = x.Sum(y => y.ShippedQty)
}).ToList();
Just try:
List<Returns> list = new List<Returns>(){ /*populate list here*/ };
list = list
.GroupBy(i => new {i.Item, i.ManCode})
.Select(g => new {g.Key.Item, g.Key.ManCode, g.Sum(i => i.ShippedQty)} );
You could use Linq to group the objects on Item and ManCode and sum the quantity like this:
var data = new List<Element>
{
new Element{ Item ="ITM01", ManCode = "A10", ShippedQty = 1},
new Element{ Item ="ITM02", ManCode = "A11", ShippedQty = 2},
new Element{ Item ="ITM01", ManCode = "A10", ShippedQty = 3},
};
var datagrp = (from q in data
group q by new { q.Item, q.ManCode } into p
select new Element
{ Item = p.Key.Item, ManCode = p.Key.ManCode,
ShippedQty = p.Sum(s => s.ShippedQty)}).ToList();
I have an array of orders within each order is an array of items. How do I group all the orders by item name and get the sum total of items ordered. In this case output would be :
Output
Item01 : quantity = 2;
Item02 : quantity = 45;
GetOrders
public Order[] GetOrders()
{
Order[] orders = new Order[]
{
new Order
{
id = 1,
orderLines = new OrderLine[]
{
new OrderLine
{
itemName = "Item 01",
quantity = 1
},
new OrderLine
{
itemName = "Item 02",
quantity = 3
},
},
},
new Order
{
id = 2,
orderLines = new OrderLine[]
{
new OrderLine
{
itemName = "Item 01",
quantity = 1
},
new OrderLine
{
itemName = "Item 02",
quantity = 42
}
}
}
};
...
I tried the following:
foreach (var order in orders)
{
foreach (var orderline in order.orderLines.GroupBy(x => x.itemName).Select(group => new
{
Metric = group.Key,
Count = group.Count()
}))
{
Console.WriteLine("{0} {1}", orderline.Metric, orderline.Count);
}
}
but it just returns 1 for each item. I am relatively new to programming , so be easy on me.Thanks
To get the sum total of all items ordered, use the following query:
var results =
(from order in orders
from orderLine in order.orderLines
group orderLine by orderLine.itemName into orderLineGrouping
let totalQuantity = orderLineGrouping.Sum(ol => ol.quantity)
select new { itemName = orderLineGrouping.Key, metric = totalQuantity }).ToList();
results.ForEach(resultItem => Console.WriteLine("{0} {1}", resultItem.itemName, resultItem.metric);
Flatten orderlines
group the item name and quantities for output string
Select the output string using grouped key and sum of grouped quantities
See code:
var output = orders.SelectMany(x => x.orderLines)
.GroupBy(x => x.itemName, x => x.quantity)
.Select(x => $"{x.Key} : quantity = {x.Sum(y => y)}");
Flatten OrderLines
You need to familiarise your self with Enumerable.SelectMany its one of the most useful methods around
SelectMany, from LINQ, collapses many elements into a single
collection. The resulting collection is of another element type. We
specify how an element is transformed into a collection of other
elements.
var summary = orders.Where(x => x.OrderLines != null) // Check for null as there seems to be null orderlines in your model
.SelectMany(x => x.OrderLines) // Flatten
.GroupBy(x => x.itemName) // Group
.Select(group => new // Project
{
ItemName = group.Key,
TotalQuantity = group.Sum(x => x.quantity)
})
.ToList(); // To List
Tip : use appropriate casing for itemName and quantity
Capitalization Conventions
The following table summarizes the capitalization rules for
identifiers and provides examples for the different types of
identifiers.
Sorry my OCD just kicked in
To illustrate my problem I have created this simple snippet. I have a class Item
public class Item
{
public int GroupID { get; set; }
public int StrategyID { get; set; }
public List<Item> SeedData()
{
return new List<Item>
{
new Item {GroupID = 1, StrategyID = 1 },
new Item {GroupID = 2, StrategyID = 1 },
new Item {GroupID = 3, StrategyID = 2 },
new Item {GroupID = 4, StrategyID = 2 },
new Item {GroupID = 5, StrategyID = 3 },
new Item {GroupID = 1, StrategyID = 3 },
};
}
}
And what I want to check is that this SeedData method is not returning any duplicated GroupID/StrategyID pairs.
So in my Main method I have this:
Item item = new Item();
var data = item.SeedData();
var groupByStrategyIdData = data.GroupBy(g => g.StrategyID).Select(v => v.Select(gr => gr.GroupID)).ToList();
for (var i = 0; i < groupByStrategyIdData.Count; i++)
{
for (var j = i + 1; j < groupByStrategyIdData.Count; j++)
{
Console.WriteLine(groupByStrategyIdData[i].Intersect(groupByStrategyIdData[j]).Any());
}
}
which is working fine but one of the problems is that I have lost the StrategyID so in my real-case scenario I won't be able to say for which StrategyID/GroupID pair I have duplication so I was wondering is it possible to cut-off the LINQ to here:
var groupByStrategyIdData = data.GroupBy(g => g.StrategyID)
and somehow perform the check on this result?
One of the very easy ways would be to do grouping using some identity for your Item. You can override Equals/GetHashCode for your Item or instead write something like:
Item item = new Item();
var data = item.SeedData();
var duplicates = data.GroupBy(x => string.Format("{0}-{1}", x.GroupID, x.StrategyID))
.Where(group => group.Count() > 1)
.Select(group => group.Key)
.ToList();
Please note, that using a string for identity inside of group by is probably not the best way to do grouping.
As of your question about "cutting" the query, you should also be able to do the following:
var groupQuery = data.GroupBy(g => g.StrategyID);
var groupList = groupQuery.Select(grp => grp.ToList()).ToList();
var groupByStrategyIdData = groupQuery.Select(v => v.Select(gr => gr.GroupID)).ToList();
You may be able to do it another way, as follows:
// Check for duplicates
if (data != null)
{
var grp =
data.GroupBy(
g =>
new
{
g.GroupID,
g.StrategyID
},
(key, group) => new
{
GroupID = key.GroupID,
StrategyId = key.StrategyID,
Count = group.Count()
});
if (grp.Any(c => c.Count > 1))
{
Console.WriteLine("Duplicate exists");
// inside the grp object, you can find which GroupID/StrategyID combo have a count > 1
}
}
I want to compare the element of a list of object ,delete the repeated Item and increment the number of the quantity of that Item (C# code ), I don't know if I should use LinQ,For or foreach statement : I have a list of OrderItem I want to delete the OrderItem that have the same FK_ArtikelId and increment the Qantity of the OrderItem . Exp:
for (int i=1 ; i < lot.Count ; i ++)
{
for (j = i + 1; j <= lot.Count; j++)
{
if (lot[i].FK_ArticleId.Equals(lot[j].FK_ArticleId))
{
lot[i].Quantity += lot[j].Quantity;
lot.Remove(lot[j]);
}
}
}
You have to use the GroupBy linq method and process the resulting groups: given the class
public class Article
{
public int FK_ArticleId { get; set; }
public int Quantity { get; set; }
}
and the following list:
var list = new List<Article>()
{
new Article() {FK_ArticleId = 1, Quantity = 10}
, new Article() {FK_ArticleId = 1, Quantity = 10}
, new Article() {FK_ArticleId = 1, Quantity = 10}
, new Article() {FK_ArticleId = 2, Quantity = 100}
, new Article() {FK_ArticleId = 2, Quantity = 100}
, new Article() {FK_ArticleId = 3, Quantity = 1000}
};
The following linq query returns what you need:
list.GroupBy(a => a.FK_ArticleId)
.Select(g => new Article() {FK_ArticleId = g.Key, Quantity = g.Sum(a => a.Quantity)});
// article id 1, quantity 30
// article id 2, quantity 200
// article id 3, quantity 1000
If you don't want to create a new article, you can take the first of the resulting group and set its Quantity to the correct value:
var results = list.GroupBy(a => a.FK_ArticleId)
.Select(g =>
{
var firstArticleOfGroup = g.First();
firstArticleOfGroup.Quantity = g.Sum(a => a.Quantity);
return firstArticleOfGroup;
});
I didn't test but this should give you an idea of the power of linq...
var stuff = lot
.GroupBy(p => p.FK_ArticleId)
.Select(g => g)
.ToList();
This should give you groups of articleIDs whereby you can easily get counts, create new consolidated lists etc.
For starters you can't use foreach because you're modifying the list and the Enumerator will throw an exception. You can do the following with Linq:
var grouped = lot.GroupBy(x => x.FK_ArticleId).ToArray();
foreach(var group in grouped)
{
group.First().Quantity = group.Sum(item => item.Quantity);
}
Now, first item in each group will contain the sum of all the quantities of items with the same FK_ArticleId. Now, to get the results use this:
var results = grouped.Select(g => g.First());
At this point it's purely your decision whether to return the results as a separate collection or insert them into the original list. If you opt for the second approach, don't forget to clear the list first:
list.Clear();
list.AddRange(results);
EDIT
A more elegant solution to accumulating the Quantity property into the first item of each group would be the following:
data.GroupBy(x=>x.FK_ArticleId)
.Select(g => g.Aggregate((acc, item) =>
{
acc.Quantity = item.Quantity;
return acc;
}));
This is what I scrapped in LinqPad:
suppose I have a list that comes from a database like this:
List<Item> list =
{
new Item
{
TypeID = 2,
Count = 5
},
new Item
{
TypeID = 2,
Count = 7
},
new Item
{
TypeID = 5,
Count = 2
}
};
I would like to sum up all elements with the same TypeID so that I have a final result list with two elements only:
List<Item> list =
{
new Item
{
TypeID = 2,
Count = 12
},
new Item
{
TypeID = 5,
Count = 2
}
};
How can I achive this using LINQ?
Cheers
Simon
list.GroupBy(x=>x.TypeID)
.Select(x=>new Item(){TypeID=x.Key,Count=x.Sum(y=>y.Count) })
.ToList();
You can use GroupBy to group by TypeID first and then do Sum on each group:
var result = list.GroupBy(x => x.TypeID)
.Select(g => new Item() {
TypeId = g.Key,
Count = g.Sum(x => x.Count)
})
.ToList();
Linq extension method Union.
var mergedList = list1.Union(list2).ToList();