How to groupby Datatable by a column and sum numeric columns - c#

I am trying to groupby a datatable by a column and sum a numeric field, but I couldnt be able to do the sum. Here is my code that groupby the datatable.
DataTable dt_grouped_by = dt_data.AsEnumerable()
.GroupBy(r => new { LOCNR = r["LOCNR"], DEPTNR = r["DEPTNR"] })
.Select(g => g.OrderBy(r => r["CODE"]).First())
.CopyToDataTable();
I have columns called quantity_received and damage_received. I need to sum those 2 columns. How can I sum them in the above code?

You have a grouping, which implements IEnumerable<DataRow>. So within that grouping you can use all LINQ extension method, like Sum():
DataTable dt_grouped_by = dt_data.AsEnumerable()
.GroupBy(r => new
{
LOCNR = r.Field<int>("LOCNR"),
DEPTNR = r.Field<int>("DEPTNR")
})
.Select(g => new
{
Code = g.First().Field<string>("CODE"),
SumQr = g.Sum(x => x.Field<int>("quantity_received"))
SumDr = g.Sum(x => x.Field<int>("damage_received"))
})
.OrderBy(x => x.Code)
.CopyToDataTable();
As you see, I prefer the Field() extension method to the row indexer row["x"], because it is strong typed. Of course I have to guess the actual data types. I also removedFirst(), because you can't convert one DataRow to a DataTable.

Related

Merge two rows into single row based on a column using LINQ C#

I have a object list like below. I want to join every two rows into single row based on column B. It is sure that only two rows would be there for every single column B value.
Input
Output
However, I have done it and solution works. but I am looking for more better solution. I am not much happy with my solution.
My solution:
var groupByItems = items.GroupBy(x => x.ColumnB).Select(x => new MappingClass
{
ColumnA= x.FirstOrDefault().ColumnA,
ColumnB= x.FirstOrDefault().ColumnB,
ColumnC= x.Where(r=> !string.IsNullOrEmpty(r.ColumnC)).Select(r=>r.ColumnC).FirstOrDefault(),
ColumnD= x.Where(r => !string.IsNullOrEmpty(r.ColumnD)).Select(r => r.ColumnD).FirstOrDefault(),
}).ToList();
Now groupByItems object returns me two rows as expected.
You can use Key of the Groups generated by GroupBy()
Also, there's no need to use .Where() you can simply put your filter as a lambda expression in .FirstOrDefault() for ColumnC & ColumnD
var groupByItems = items.GroupBy(x => new { ColumnA = x.ColumnA, ColumnB = x.ColumnB })
.Select(x => new MappingClass
{
ColumnA = x.Key.ColumnA,
ColumnB = x.Key.ColumnB,
ColumnC = x.FirstOrDefault(m => !string.IsNullOrEmpty(m.ColumnC)).ColumnC,
ColumnD = x.FirstOrDefault(m => !string.IsNullOrEmpty(m.ColumnD)).ColumnD
})
.ToList();

Return a List of distinct values from DataGridView

As the topic says: Is there a way to return a list of distinct values from a certain Column of a DataGridView?
This should do what you asked for:
var vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Select(x => x.Cells[yourColumn].Value.ToString())
.Distinct()
.ToList();
Note that the simple version above assumes that there are only valid values. If you also may have new rows or empty cells you may want to expanded it like this:
var vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells[column].Value != null) //..or or both
.Select(x => x.Cells[column].Value.ToString())
.Distinct()
.ToList();

LINQ - How to get subset of columns after GroupBy

This LINQ-to-SQL query works (testing in LINQpad):
var q5 = LOGs.Where(r => r.APP_NAME == "Toaster")
.GroupBy(pol => pol.CASE_NO)
.Select(grp => grp.First())
.OrderByDescending(l => l.WHEN);
q5.Dump();
However, that returns all columns for each row.
How can I refine the Select() part to specify certain columns?
I can do it in two steps by adding .ToList() to the query, then querying q5:
var q5a = q5.Select(r => new {CASE=r.CASE_NO, WHEN = r.WHEN});
q5a.Dump();
Can I accomplish that in one statement instead of two?
Thanks --
why don't you filter after where?
var q5 = LOGs.Where(r => r.APP_NAME == "Toaster")
.Select(r=> new{r.CASE_NO, r.WHEN})
.GroupBy(pol => pol.CASE_NO)
.Select(grp => grp.First())
.OrderByDescending(l => l.WHEN);
remembar that new {CASE=r.CASE_NO, WHEN = r.WHEN} creates a new anonymous type because of differents property names, new {r.CASE_NO, r.WHEN} doesn't !

getting values of DataTable column and putting it into new datatable C# using LINQ

I Have a datable that I use to store ingredient recipes. I have some code that finds if there are similar ingredient names and if there are, I then sum up the weight and add it to the table.
Now i want to be able to add some few other columns that also exist in the table.
Here is what i currently have
protected DataTable cleanDataTable(DataTable dt)
{
var result = dt.AsEnumerable()
.GroupBy(r => r.Field<string>("Ingredients"))
.Select(g =>
{
var row = dt.NewRow();
row.ItemArray = new object[]
{
g.Key,
g.Sum(r => double.Parse(r.Field<string>("KG")))
};
return row;
}).CopyToDataTable();
dt = result;
return dt;
}
I have the following columns, BakerP, FlourP, BatchP, how can i get the value of each column and save it into the datatable using the code below with Linq?
I don't know what data types BakerP, FlourP, or BatchP are, so the following just lists some options:
If the fields are numeric, and you want to summarize them like you did the weight, you would do the same kind of thing you did with the weight in order to add those fields to the row :
var row = dt.NewRow();
row.ItemArray = new object[]
{
g.Key,
g.Sum(r => double.Parse(r.Field<string>("KG"))),
g.Sum(r => double.Parse(r.Field<string>("BakerP"))), // assuming this is also a number saved as a string
// etc.
};
return row;
If they are not numeric, or you don't want to summarize them, you can group on them, as you did with Ingredients:
.GroupBy(r => new {
Ingredients = r.Field<string>("Ingredients"),
BakerP = r.Field<string>("BakerP"),
FlourP = r.Field<string>("FlourP"),
BatchP = r.Field<string>("BatchP")
})
.Select(g =>
{
var row = dt.NewRow();
row.ItemArray = new object[]
{
g.Key.Ingredients,
g.Key.BakerP,
g.Key.FlourP,
g.Key.BatchP,
g.Sum(r => double.Parse(r.Field<string>("KG")))
};
return row;
}).CopyToDataTable();
Or you can mix and match, grouping on one field while summarizing another.
The key thing, though: since you're using GroupBy, all the fields in your output need to either be part of what you group by, or be summarized in some way (with .Sum() or .Average() or .Max() or .First(), etc.) So if you want these additional fields in your output, you need to decide which way you want to handle them.

Use LINQ to find duplicated rows (with list of specified columns)

I use the code below to get the duplicated rows for 3 columns: String, Date, Money.
I wonder if there is any general method that I can input a dynamic List of column name in this LINQ to find duplicated rows?
DataTable allDuplicates = dt.AsEnumerable()
.GroupBy(dr => new
{
Field1 = dr.Field<object>("String"),
Field2 = dr.Field<object>("Date"),
Field3 = dr.Field<object>("Money"),
})
.Where(g => g.Count() > 1)
.SelectMany(g => g)
.ToList().CopyToDataTable();
}
How about with a custom ArrayEqualityComparer<T> type (such as the one listed here):
string[] colsToConsider = ...
var allDuplicates = dt.AsEnumerable()
.GroupBy(dr => colsToConsider.Select(dr.Field<object>)
.ToArray(),
new ArrayEqualityComparer<object>())
.Where(g => g.Count() > 1)
.SelectMany(g => g)
.CopyToDataTable();
You can also consider using a Dictionary<TKey, TValue> (and an associated dictionary-comparer) if you find the implicit use of array indices here hackish.
while execute above code.
The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly

Categories