Use LINQ to group data from DataTable - c#

I want to use LINQ to group data from a DataTable (columns: userid, chargetag, charge).
The content could look like this:
userid chargetag charge
-----------------------------
user1 tag3 100
user2 tag3 100
user3 tag5 250
I need something like this as a result:
chargetag count sum
-------------------------
tag3 2 200
tag5 1 250
This is what I have so far:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let count = g.Count()
select new
{
ChargeTag = g.Key,
Count = count,
};
I can extract the name of the chargetag and the number of it.
How would I have to change the LINQ query to access the sum of charges as well?
Thanks in advance :-)
Regards,
Kevin

That's pretty easy - just use the Sum extension method on the group.
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
Count = g.Count(),
ChargeSum = g.Sum(x => x.Field<int>("charge"))
};
(I've removed the let clause here as it wasn't really buying you anything.)
Now that may be inefficient; it may end up grouping twice in order to perform two aggregation operations. You could fix that like with a query continuation like this, if you really wanted:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
List = g.ToList(),
} into g
select new
{
g.ChargeTag,
Count = g.List.Count,
ChargeSum = g.List.Sum(x => x.Field<int>("charge"))
};
Or with a let clause instead:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let list = g.ToList()
select new
{
ChargeTag = g.Key,
Count = list.Count,
ChargeSum = list.Sum(x => x.Field<int>("charge"))
};

Related

LINQ to SQL: Group, Count, Sum. I'm so confused

Good morning all,
I have been stuck on this all morning and feel like I've hit a wall. I'd love any advice that can be given at this point.
My table is basically as follows:
PatientName|LivingSpace
-----------|-----------
Patient 1 | Unit 1
Patient 2 | Unit 1
Patient 3 | Unit 2
Patient 4 | Unit 2
Patient 5 | Unit 3
Patient 6 | Unit 3
Patient 7 | Unit 3
Patient 8 | Unit 3
I need a LINQ to SQL query to illustrate this:
Unit|Count
----|-----
Unit 1 | 2
Unit 2 | 2
Unit 3 | 4
TOTAL | 8
My SQL query works fine, I'm just having issues with converting it to LINQ:
SELECT LivingSpace, COUNT(LivingSpace) AS LivingSpace
FROM PatientTable
WHERE Status = 'Active'
GROUP BY LivingSpace
UNION ALL
SELECT 'SUM' LivingSpace, COUNT(LivingSpace)
FROM PatientTable
var counts = from x in ctx.PatientTable
group x by x.LivingSpace into y
select new { Key = y.Key Count = y.Count() };
var total = new { Key = "Total" , Count = ctx.PatientTable.Count() };
var full = counts.ToList();
full.Add(total);
If you want to do it all in one query the following should work (adjusting for the actual names of your properties of course).
context.PatientTable.GroupBy(a => a.LivingSpace.Name, a => 1)
.Select(a => new
{
a.Key,
Total = a.Sum(q => q)
})
.Union(PatientTable.Select(a => new
{
Key = "Total",
Total = PatientTable.Count()
}))
var report = patients
.GroupBy(p => p.LivingSpace)
.Select(g => new
{
Unit = g.Key,
Count = g.Count()
})
.Union(patients
.Select(p => new
{
Unit = "Total",
Count = patients.Count
}));
Something like this should work and just run one query.
var results = db.PatientTable
.GroupBy(p => p.LivingSpace)
.Select(grp => new
{
Unit = grp.Key,
Count = grp.Count()
})
.Union(db.PatientTable
.GroupBy(p => 1)
.Select(grp => new
{
Unit = "Total",
Count = grp.Count()
}));
I see you got the answer, but for learning purposes, here is side by side conversion.
Your SQL (with some aliases added for better comparison)
SELECT P.LivingSpace, COUNT(P.*) AS Count
FROM PatientTable AS P
WHERE P.Status = 'Active'
GROUP BY P.LivingSpace
UNION ALL
SELECT 'SUM' AS LivingSpace, COUNT(P.*) AS Count
FROM PatientTable AS P
The same single query in LINQ
var query =
(
from p in db.PatientTable
where p.Status = "Active"
group p by p.LivingSpace into g
select new { LivingSpace = g.Key, Count = g.Count() }
)
.Concat
(
from p in db.PatientTable
group p by "SUM" into g
select new { LivingSpace = g.Key, Count = g.Count() }
);

Adding more items to a group by statement

I have this query:
var rowsPerProvider = (from row in dt.Select()
let emp = row["f_name"].ToString().Trim()
group row by emp
into g
select g).ToDictionary(
g => g.Key,
g => g.ToArray());
How can I update it to also filter on some more columns? for example currently it is on f_name. How can I update it to group on f_name and m_name and l_name?
Make an anonymous object containing the fields you want to group by:
var rowsPerProvider = (from row in dt.Select()
group row by new
{
emp1 = row["f_name"].ToString().Trim(),
emp2 = row["m_name"].ToString().Trim(),
emp3 = row["l_name"].ToString().Trim(),
}
into g
select g).ToDictionary(
g => g.Key,
g => g.ToArray());
Use anonymous class:
// (...)
group row by new { emp, something }

LINQ select multiple columns as separate array

I have table 4 columns.
JobId StateId Salary Expense
1 1 35,000 31,000
1 1 33,000 25,000
1 2 28,000 26,000
2 2 7,000 16,000
2 2 6,000 20,000
2 1 9,000 22,000
2 1 15,000 29,000
By using LINQ in C#, i want to group by JobId and StateId combination.For each combination i want an array of Salary and array of Expense.
I can get one column as a array by for each combination, by using this
(from r in myTable.AsEnumerable()
group r by new {
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
}).ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l=> (from i in l select i.Field<double>("Salary")).AsEnumerable()
);
How can i have Salary and Expense in two array for each group??
My goal is to find average Salary and average Expense for each combination and do some other operation. Or at least tell me how can select multiple columns as separate array.
Note: I don't want collection of anonymous objects for each combination.
To select two different columns as collections in your query you can do this:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
Salaries = g.Select(x => x.Field<double>("Salary")),
Expenses = g.Select(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.Salaries, l.Expenses }
);
Then you can compute the averages fairly easily:
var averageSalary = result[...].Salaries.Average();
var averageExpense = result[...].Expenses.Average();
But if all you really need is the averages, this will work:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
AverageSalary = g.Average(x => x.Field<double>("Salary")),
AverageExpense = g.Average(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.AverageSalary, l.AverageExpense }
);
Do not use LINQ for this kind of statement. If you need to count the groupings and the compute some sort of average salary/expense you could try a list:
List<myType> myList = new List<myType>();
//add stuff to myList
List<myType> JobID1 = new List<myType();
List<myType> JobID2 = new List<myType();
foreach(var item in myList)
{
if(item.JobID == 1)
JobID1.add(item);
if(item.JobID == 2)
JobID2.add(item);
}
int avgSalOne;
foreach(var item in JobID1)
{
avgSalOne += item.Salary;
}
avgSalOne = avgSaleOne / JobID2.Count;
//Note that you get Job Id 2 average salary the same way, and also the Expense by changing item. Salary to item.Expense

How to get a sum of children values on one LINQ

This is the structure I have:
Program
- Description, etc...
Action
- Program_Id, Description, etc..
Cost
- Action_Id, Value1, Value2, Value3
One Action can Have multiple Costs.
What I Need is a query that group this values by Program. Like:
"Program name" | Total of Value1 | Total of Value 2 | Total of the program
This is my effort so far:
var ListByPrograma = from a in db.Actions
join c in db.Costs on a.Id equals c.Action_Id
group a by a.Program into p
select new
{
Program = p.Key,
actionsQuantity = p.Count(),
totalValue1 = p.Costs.????
totalValue2 = ?,
totalByProgram = ?
};
Does something like this work?
var ListByPrograma = from a in db.Actions
join c in db.Costs on a.ID equals c.Action_Id
group new {a,c} by a.Program into p
select new
{
Program = p.Key,
actionsQty = p.Count ( ),
totalValue1 = p.Sum(y => y.c.Value1),
totalValue2 = p.Sum (y => y.c.Value2),
totalValue3 = p.Sum(y=>y.c.Value3)
};

How to select multiple values after using Max() in LINQ to Objects?

I have the following LINQ query:
var query =
(from p in obj1
group p by p.objID into g
let totalSum = g.Sum(p => p.ObjPrice)
select new { MyObjectID = g.Key, totalSum })
.Max(g => g.totalSum);
I want to select both the object id and price of the object with the maximum price. How can I do that?
Use an order by descending clause and call FirstOrDefault().
(from p in obj1
group p by p.objID into g
let totalSum = g.Sum(p => p.ObjPrice)
orderby totalSum descending
select new { MyObjectID = g.Key, totalSum }).FirstOrDefault();

Categories