How to group by then sum inside the list
below is my sample code:
List<BrandType> brandTypeList = new List<BrandType>();
BrandType brandTypeClass = new BrandType();
brandTypeClass.Amount = 100;
brandTypeClass.Count = 50;
brandTypeClass.Category = "Fish";
brandTypeList.Add(brandTypeClass);
BrandType brandTypeClass2 = new BrandType();
brandTypeClass2.Amount = 100;
brandTypeClass2.Count = 50;
brandTypeClass2.Category = "Fish";
brandTypeList.Add(brandTypeClass2);
BrandType brandTypeClass3 = new BrandType();
brandTypeClass3.Amount = 100;
brandTypeClass3.Count = 50;
brandTypeClass3.Category = "Pork";
brandTypeList.Add(brandTypeClass3);
brandTypeList.GroupBy(x => new { x.Category }).Select
(x => new { Category = x.Key.Category,
Amount = x.Sum(z => z.Amount),
Count = x.Sum(z => z.Count) });
Here's what it looks like
[0] {Amount = 100, Category = "Pork", Count = 50}
[1] {Amount = 100, Category = "Fish", Count = 50}
[2] {Amount = 100, Category = "Fish", Count = 50}
How can I SUM the amout and count then group by Category?
I want the result to be
Amount = 200, Category = "Fish", Count = 100
Amount = 100, Category = "Pork" Count = 50
The following code snippet will work for you:
var result = from brand in brandTypeList
group brand by brand.Category into grp
select new
{
Category = grp.Key,
Amount = grp.Sum(z => z.Amount),
Count = grp.Sum(z => z.Count)
};
This one also works fine:
var result = brandTypeList.GroupBy(x => x.Category).Select
(y => new {
Category = y.Key,
Amount = y.Sum(z => z.Amount),
Count = y.Sum(z => z.Count)
});
It turns out that your code also works fine, probably the problem is that you expect list to be modified inplace, but linq does not produce side effects and new IEnumerable will be created and you need to save results to variable.
Related
I would like to get the total order amount for each customer with Linq, I know I need to group and sum I have only succeeded to group without summing the whole amount for each order.
var OrderByCustumer = new[] {
new { name = "cust1", order = 400 },
new { name = "cust1", order = 250 },
new { name = "cust1", order = 130 },
new { name = "cust2", order = 30 },
new { name = "cust3", order = 205}
};
var res= OrderByCustumer.GroupBy(x=>x.name).Select((x,y)=>new{
a=x.Key
});
foreach(var a in res){
Console.WriteLine(a);
}
.**
OutPut
a = cust1
a = cust2
a = cust3
**
Try this
var res = OrderByCustumer.GroupBy(x => x.name).Select(x => new {
a = x.Key,
sum = x.Sum(c => c.order)
});
foreach (var item in res)
{
Console.WriteLine($"{ item.a} - Sum = {item.sum}");
}
I have three tables named Deposit, Debit and Transfer,
like
Deposit { DepositID, DepostDate, Amount}
Debit { DebitID, DebitDate, Amount}
Transfer { TransferID, TransferDate, Amount}
How can I show all three tables in one chart? I am even wondering if it is better to put those three tables into one table instead, like
Transaction {TransactionId, TransactionTypeId, TransactionDate, Amount}
where TransactiontypeId could be 1 = for Deposit, 2 for Debit and 3 for Transfer and bind this transaction table to the chart.
Let's say I have all those in one table instead and with table name Transactions then #mm8 helped me figure this out:
var result = (from pr in db.Transactions
join tr in db.TransactionType on pr.TrTypeId equals tr.TransactionTypeId
select new
{
TransactionDate = pr.TransactionDate,
TransactionType = tr.TrType,
Amount = pr.Amount
}).ToList();
chart1.DataSource = result
.GroupBy(x => x.TransactionDate.Value.Year)
.Select(g => new
{
Year = g.Key,
TransactionType = g. //////
Amount = g.Sum(y => y.Amount)
})
.ToArray();
Is is better to have a chart from one table or from multiple tables and how to do multiple.
I am aware that I have to create different series for every table like this:
var Depseries = chart1.Series.Add("Deposit");
Depseries.XValueMember = "Year";
Depseries.YValueMembers = "DepositAmount";
Depseries.Name = "Deposit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Deposit"].IsValueShownAsLabel = true;
Depseries.CustomProperties = "LabelStyle=Left";
// Debit
var Debseries = chart1.Series.Add("Debit");
Debseries.XValueMember = "Year";
Debseries.YValueMembers = "DebitAmount";
Debseries.Name = "Debit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Debit"].IsValueShownAsLabel = true;
Debseries.CustomProperties = "LabelStyle=Left";
// Transfer
var FDseries = chart1.Series.Add("Transfer");
FDseries.XValueMember = "Year";
FDseries.YValueMembers = "TransferAmount";
FDseries.Name = "Transfer";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Transfer"].IsValueShownAsLabel = true;
FDseries.CustomProperties = "LabelStyle=Left";
You could just select the data from each of the tables and then use the DataBind method to populate the series with data, e.g.:
var deposits = (from x in db.Deposits select new { x.DepositDate, x.Amount })
.ToArray()
.GroupBy(x => x.DepositDate.Year)
.Select(g => new { Year = g.Key, Amount = g.Sum(y => y.Amount) })
.ToArray();
var Depseries = chart1.Series.Add("Deposit");
Depseries.XValueMember = "Year";
Depseries.YValueMembers = "DepositAmount";
Depseries.Name = "Deposit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Deposit"].IsValueShownAsLabel = true;
Depseries.CustomProperties = "LabelStyle=Left";
chart1.Series["Deposit"].Points.DataBind(deposits, "Year", "Amount", null);
var debits = (from x in db.Debits select new { x.DebitDate, x.Amount })
.ToArray()
.GroupBy(x => x.DebitDate.Year)
.Select(g => new { Year = g.Key, Amount = g.Sum(y => y.Amount) })
.ToArray();
var Debseries = chart1.Series.Add("Debit");
Debseries.XValueMember = "Year";
Debseries.YValueMembers = "DebitAmount";
Debseries.Name = "Debit";
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1;
chart1.Series["Debit"].IsValueShownAsLabel = true;
Debseries.CustomProperties = "LabelStyle=Left";
chart1.Series["Debit"].Points.DataBind(debits, "Year", "Amount", null);
...
I've been working on this for a while and have tried a couple of different ways. I'm trying to group like items (i.e. StartWith 60 or 20) as part of the output but it doesn't work when I try StartsWith in Groupby section.
Also... instead of using 60 or 20 on the output, is there a way to rename to 60 = Vendor and 20 = Internal?
var query = referenceDt.AsEnumerable()
.Where(results => results.Field<Int32>("FaultCode").ToString().StartsWith("60") ||
results.Field<Int32>("FaultCode").ToString().StartsWith("20")
)
.GroupBy(results => new
{
//FaultCode = results.Field<int>("FaultCode")
FaultCode = results.Field<Int32>("FaultCode").ToString().StartsWith("60") ||
results.Field<Int32>("FaultCode").ToString().StartsWith("20")
})
.OrderBy(newFaultCodes => newFaultCodes.Key.FaultCode)
.Select(newFaultCodes => new
{
FaultCode = newFaultCodes.Key.FaultCode,
Count = newFaultCodes.Count()
})
;
Try something like:
.GroupBy(results => new
{
FaultCode = results.Field<Int32>("FaultCode").ToString().StartsWith("60") ? 60 : 20
})
And
.Select(newFaultCodes => new
{
FaultCode = newFaultCodes.Key.FaultCode.ToString().StartsWith("60")
? "Vendor" : "Internal",
Count = newFaultCodes.Count()
})
You should have a mapping of all prefixes somewhere and map the the corresponding value. You can use a dictionary or join with another table.
e.g.,
var faultTypes = new Dictionary<string, string>
{
["20"] = "Internal",
["60"] = "Vendor",
};
var query =
from r in referenceDt.AsEnumerable()
let faultCode = r.Field<int>("FaultCode")
let faultCodeStr = Convert.ToString(faultCode).Substring(0, 2)
where new[] { "20", "60" }.Contains(faultCodeStr)
group faultCode by faultTypes[faultCodeStr] into g
select new
{
FaultType = g.Key,
Count = g.Count(),
};
I have two rows which have all the data same except one column.
I want to show only one row on the UI but one row which has different data should be shown as comma seperated values.
Sample Data
PricingID Name Age Group
1 abc 56 P1
1 abc 56 P2
Output should be :
PricingID Name Age Group
1 abc 56 P1,P2
I am using this approach but it is not working , it gives me two rows only but data i am able to concatenate with comma.
List<PricingDetailExtended> pricingDetailExtendeds = _storedProcedures.GetPricingAssignment(pricingScenarioName, regionCode, productCode, stateCode, UserId, PricingId).ToList();
var pricngtemp = pricingDetailExtendeds.Select(e => new
{
PricingID = e.PricingID,
OpportunityID = e.OpportunityID,
ProductName = e.ProductName,
ProductCD = e.ProductCD
});
pricingDetailExtendeds.ForEach(e=>
{
e.ProductCD = string.Join(",",string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductCD).ToArray())).Split(',').Distinct().ToArray());
e.OpportunityID =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.OpportunityID).ToArray())).Split(',').Distinct().ToArray());
e.ProductName =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductName).ToArray())).Split(',').Distinct().ToArray());
}
);
// pricingDetailExtendeds = GetUniquePricingList(pricingDetailExtendeds);
return pricingDetailExtendeds.Distinct().AsEnumerable();
Any body can suggest me better approach and how to fix this issue ?
Any help is appreciated.
You want to use the GroupBy linq function.
I then use the String.Join function to make the groups comma seperated.
So something like this:
var pricingDetailExtendeds = new[]
{
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P1"
},
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P2"
}
};
var pricngtemp =
pricingDetailExtendeds.GroupBy(pde => new {pde.PricingID, pde.Name, pde.Age})
.Select(g => new {g.Key, TheGroups = String.Join(",", g.Select(s => s.Group))}).ToList();
You can easily extrapolate this to the other fields.
To return the PricingDetailExtended, the just create it in the select. So something like this
.Select(g => new PricingDetailExtended {
PricingID = g.Key.PricingId,
TheGroups = String.Join(",", g.Select(s => s.Group))
}).ToList();
You won't have the field TheGroups though, so just replace that field with the proper one.
An example of what I was describing in my comment would be something along the lines of the following. I would expect this to be moved into a helper function.
List<PriceDetail> list = new List<PriceDetail>
{
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P1"},
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P2"},
new PriceDetail {Id = 2, Age = 56, Name = "abc", group = "P1"}
};
Dictionary<PriceDetailKey, StringBuilder> group = new Dictionary<PriceDetailKey, StringBuilder>();
for (int i = 0; i < list.Count; ++i)
{
var key = new PriceDetailKey { Id = list[i].Id, Age = list[i].Age, Name = list[i].Name };
if (group.ContainsKey(key))
{
group[key].Append(",");
group[key].Append(list[i].group);
}
else
{
group[key] = new StringBuilder();
group[key].Append(list[i].group);
}
}
List<PriceDetail> retList = new List<PriceDetail>();
foreach (KeyValuePair<PriceDetailKey, StringBuilder> kvp in group)
{
retList.Add(new PriceDetail{Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
}
you could even convert the final loop into a LINQ expression like:
group.Select(kvp => new PriceDetail {Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
Its worth noting you could do something similar without the overhead of constructing new objects if, for example, you wrote a custom equality comparer and used a list instead of dictionary. The upside of that is that when you were finished, it would be your return value without having to do another iteration.
There are several different ways to get the results. You could even do the grouping in SQL.
I have the following code:
var workOrderList = new List<WorkOrder>(
from index in Enumerable.Range(1, orders.Length)
select new WorkOrder
{
OrderID = orders[index - 1],
Status = status[random.Next(0,status.Length-1)],
TotalQuantity = random.Next(1, 5) * 8,
ScheduleCollection = new ObservableCollection<Schedule>
{
new Schedule
{
Color = colors[random.Next(0,colors.Length-1)],
Model = models[random.Next(0,models.Length-1)],
Status = status[random.Next(0,status.Length-1)],
TotalNumber = To be Updated bases on Total Quantity
}
}
Now I want to update Total Number by either dividing or subtracting value from TotalQuantity .
Use a let clause in your query to extract common expressions:
var workOrderList = new List<WorkOrder>(
from index in Enumerable.Range(1, orders.Length)
let totalQuantity = random.Next(1, 5) * 8
select new WorkOrder
{
OrderID = orders[index - 1],
Status = status[random.Next(0,status.Length-1)],
TotalQuantity = totalQuantity,
ScheduleCollection = new ObservableCollection<Schedule>
{
new Schedule
{
Color = colors[random.Next(0,colors.Length-1)],
Model = models[random.Next(0,models.Length-1)],
Status = status[random.Next(0,status.Length-1)],
TotalNumber = // Do something with totalQuantity
}
}
});