Linq Objects Group By & Sum - c#

I have a datatable with a column "No" and "Total" column. I'd like to bring back the sum of the "Total" column for each "No". I've tried the below, but I'm getting several errors as I'm struggling with the syntax.
var Values =
(from data in DtSet.Tables["tblDetails"].AsEnumerable()
group data by data.Field<"No">
select new
{
name_1 = data.Field<double>("No"),
name_2 = data.Field<double>("Total"),
}
);

This will give you sum of Total fields in name_2 property, and grouping No in name_1 property (I think you need better naming here)
var Values = from row in DtSet.Tables["tblDetails"].AsEnumerable()
group row by row.Field<double>("No") into g
select new
{
name_1 = g.Key,
name_2 = g.Sum(r => r.Field<double>("Total"))
};
Consider about names No and TotalSum instead.

You start using linq, but good old DataTable has it's own way:
var total = DtSet.Tables["tblDetails"].Compute("sum(Total)", "No = x");
I have to leave it with the x in it, because I don't know the values of the "No" column. The part "No = x" is a filter. If it is null or an empty string all rows will be used in the computation.

Related

How to select from a datatable where the column is part of a string in C#.net

I have a datatable that I am trying to make an update on.
my datatable is the data source of a data gridview (Forms application)
I want to update all rows that are part of a textbox
the textbox contains a comma separated values such as
A1,A11,B4,B38,C44
I have this code but stuck on how to make it working
DataTable dt = new DataTable();
dt = (DataTable)grd1.DataSource;
DataRow[] dr = dt.Select("'," + TextBox1.Text + ",' LIKE '%,Code,%'");
foreach (DataRow row in dr)
{
row["Price"] = 1000;
}
The problem is in this code
"'," + TextBox1.Text + ",' LIKE '%,Code,%'"
it does not retuen any rows so I think I did not write it the right way.
How to fix my select line?
Note : I added a comma before and after so I do not get "T37" when I am looking for "T3"
Your question wasn't easy to understand for me, but you seem to be saying that you will type a list of values into the textbox and these values are to be looked up in the [Code] column of the datatable. I'm not clear on whether the Code column itself is a single value or a comma separated list of codes, so I'll answer for both. Assuming the Code column is a CSV, and you want that any row where any one of the values in Code is one of these values in the textbox, shall have its price updated to 1000:
So for a textbox of "A1,B1" and a datarows like:
Code Price
A1,C3 200
B4,C7 400
The 200 row shall be updated and the 490 row shall not
I'd use LINQ for this rather than datatable select
var codes = textbox.Split(',');
var rows = dt.AsEnumerable().Where(r => codes.Any(c => (r["Code"] as string).Split(',').Contains(c)));
foreach(var r in rows)
r["Price") = 1000;
If you're doing this a lot I wouldn't have the codes in the row as a CSV string; a row field is allowed to be an array of strings - storing the codes as an array in the row will save having to split them every time you want to query them
If I've got this wrong and the row contains just a single Code value, the logic is the same, it just doesn't need the split(though the code above would work, it's not optimal):
var rows = dt.AsEnumerable().Where(r => codes.Any(c => (r["Code"] as string) == c));
And actually if you're going to be doing this a lot, I would index the datatable:
//if it's a csv in the datatable
var index = dt.AsEnumerable()
.SelectMany(r => r["Code"].ToString().Split(','), (row, code) => new { R=row, C=code})
.ToLookup(o => o.C, o => o.R);
This will give something like a dictionary where a code maps to a list of rows where the code appears. For a row set like
Code Price
A1,C3 200
B4,C3 400
You get a "dictionary" like:
A1: { "A1,C3", 200 }
C3: { "A1,C3", 200 },{ "B4,C3", 400 }
B4: { "B4,C3", 400 }
so you could:
foreach(var c in codesTextbox.Split)
foreach(var row in index["c"])
row["Price"] = 1000;
If the Code column doesn't contain a csv, doing a selectmany should still be fine, but to optimize it:
var index = dt.AsEnumerable().ToLookup(r => (string)r["Code"]);

How to load a specific value from a database to dropdownlist?

I have a query which returns a data. Actually it has to return only one row of data, but i have to take several records from different tables and they contain several values for one primary attribute. There according to the query I get several rows. Now I want to load one of the records(one cell of a column) to a dropdownlist. How can I do it?
This is my query
select emp.[App_no]
,emp.[EMP_CALLING_NAME]
,emp.[EMP_INI]
,emp.[EMP_SURNAME]
,emp.[EMP_TITLE]
,emp.[EMP_NAMES_INI]
,emp.[EMP_FULLNAME]
,emp.[EMP_NIC]
,emp.[EMP_dob]
,emp.[EMP_GENDER]
,emp.[NAT_CODE]
,emp.[EMP_MARITAL_STATUS]
,emp.[EMP_DATE_JOINED]
,emp.[EMP_CONFIRM]
,emp.[CX_CODE]
,emp.[DSMG_CODE]
,emp.[CAQT_CODE]
,emp.[EMP_PER_ADDRESS1]
,emp.[EMP_PER_ADDRESS2]
,emp.[EMP_PER_ADDRESS3]
,emp.[EMP_PER_CITY]
,emp.[EMP_PER_TELEPHONE]
,emp.[EMP_PER_MOBILE]
,emp.[EMP_PER_PROVINCE]
,emp.[EMP_PER_DISTRICT]
,emp.[EMP_TEM_ADDRESS1]
,emp.[EMP_TEM_ADDRESS2]
,emp.[EMP_PER_ELECTORATE]
,emp.[EMP_TEM_ADDRESS3]
,emp.[EMP_TEM_CITY]
,emp.[EMP_PER_GD]
,bank.[BBRANCH_CODE]
,bank.[EBANK_ACC_NO]
,bank.[EBANK_ACC_TYPE_FLG]
,bank.[EBANK_ACTIVE_FLAG]
,bank.[LAST_MODIFIED_DATE]
,qual.[Qulif_code]
,lang.[lang_code]
,lang.[ability_type]
,ex.[From_date]
,ex.[To_date]
,ex.[Organization]
,ex.[designation]
,ex.[Cobtact_No]
,nation.[NAT_NAME]
from [EMPLOYEE] emp, [EMP_BANK] bank,
[QUALIF] qual, [LANG] lang,
[Experience] ex, [NATIONALITY] nation
where emp.App_no = bank.App_no and emp.App_no = qual.App_No
and emp.App_no = lang.App_no and nation.NAT_CODE = emp.[NAT_CODE]
and emp.App_no = ex.App_no and emp.App_no=#num
This is how I bind data
ddlDesignations.DataSource = ds.Tables[0];
ddlDesignations.DataTextField = "designation";
ddlDesignations.DataValueField = "designation";
ddlDesignations.DataBind();
This is how the dropdownlist appears
This might do the trick
DataView view = new DataView(ds.Tables[0]);
DataTable distinctValues = view.ToTable(true, "designation");
ddlDesignations.DataSource = distinctValues;
ddlDesignations.DataTextField = "designation";
ddlDesignations.DataValueField = "designation";
ddlDesignations.DataBind();
Here, first parameter in ToTable() is a boolean which indicates whether you want distinct rows or not. and the second parameter in the ToTable() is the column name based on which we have to select distinct rows. Only these columns will be in the returned datatable.
You can use LINQ for this
var distinctdesignations = ds.Tables[0].AsEnumerable()
.Select(row => new {
designation = row.Field<string>("designation")
})
.Distinct();
ddlFamilyMembers.DataTextField = "designation";
ddlFamilyMembers.DataValueField = "designation;
ddlFamilyMembers.DataSource = distinctdesignations;
ddlFamilyMembers.DataBind();

How do I use LINQ to update a datatable with a SqlDataReader?

I am trying to merge data from two separate queries using C#. The data is located on separate servers or I would just combine the queries. I want to update the data in one of the columns of the first data set with the data in one of the columns of the second data set, joining on a different column.
Here is what I have so far:
ds.Tables[3].Columns[2].ReadOnly = false;
List<object> table = new List<object>();
table = ds.Tables[3].AsEnumerable().Select(r => r[2] = reader.AsEnumerable().Where(s => r[3] == s[0])).ToList();
The ToList() is just for debugging. To summarize, ds.Tables[3].Rows[2] is the column I want to update. ds.Tables[3].Rows[3] contains the key I want to join to.
In the reader, the first column contains the matching key to ds.Tables[3].Rows[3] and the second column contains the data with which I want to update ds.Tables[3].Rows[2].
The error I keep getting is
Unable to cast object of type 'WhereEnumerableIterator1[System.Data.IDataRecord]' to type 'System.IConvertible'.Couldn't store <System.Linq.Enumerable+WhereEnumerableIterator1[System.Data.IDataRecord]> in Quoting Dealers Column. Expected type is Int32.
Where am I going wrong with my LINQ?
EDIT:
I updated the line where the updating is happening
table = ds.Tables[3].AsEnumerable().Select(r => r[2] = reader.AsEnumerable().First(s => r[3] == s[0])[1]).ToList();
but now I keep getting
Sequence contains no matching element
For the record, the sequence does contain a matching element.
You can use the following sample to achieve the join and update operation. Let's suppose there are two Datatables:
tbl1:
tbl2:
Joining two tables and updating the value of column "name1" of tbl1 from column "name2" of tbl2.
public DataTable JoinAndUpdate(DataTable tbl1, DataTable tbl2)
{
// for demo purpose I have created a clone of tbl1.
// you can define a custom schema, if needed.
DataTable dtResult = tbl1.Clone();
var result = from dataRows1 in tbl1.AsEnumerable()
join dataRows2 in tbl2.AsEnumerable()
on dataRows1.Field<int>("ID") equals dataRows2.Field<int>("ID") into lj
from reader in lj
select new object[]
{
dataRows1.Field<int>("ID"), // ID from table 1
reader.Field<string>("name2"), // Updated column value from table 2
dataRows1.Field<int>("age")
// .. here comes the rest of the fields from table 1.
};
// Load the results in the table
result.ToList().ForEach(row => dtResult.LoadDataRow(row, false));
return dtResult;
}
Here's the result:
After considering what #DStanley said about LINQ, I abandoned it and went with a foreach statement. See code below:
ds.Tables[3].Columns[2].ReadOnly = false;
while (reader.Read())
{
foreach (DataRow item in ds.Tables[3].Rows)
{
if ((Guid)item[3] == reader.GetGuid(0))
{
item[2] = reader.GetInt32(1);
}
}
}

How to perform addition in a Linq query

My query is like
var query = dbContext.table1.join(dbcontext.table2,i=>i.table1.id,j=>j.table2.id,
(i,j)=>new {
name = i.name,
hours = (new decimal?[]{ j.day1,j.day2,j.day3}.Sum()),
total = ???????
}).ToArray();
In the hours field I am getting the values of individual user's working hours for three days. In the "total" field I want to display the sum of all users' "hours" values.
Can you tell me how to get the "total" value?
var total = query.Sum(x => x.hours);
Since this total is for all rows in the result set, you do not want one value for each row, but one value representing the aggregate of the entire array.

Getting duplicates count for each distinct value from a datatable

I've a datatable which has a single text column 'Title' which can have multiple values with duplicates. I can remove the duplicates using a dataview.
DataView v = new DataView(tempTable);
tempTable = v.ToTable(true, "Title");
But how can i get the number of duplicates for each distinct value without any looping?
If you don't want to loop or use Linq, so there is no way to do that but you can use a computed column on the data table with one more condition if applicable with you. That is the data should be in two related tables like this.
DataRelation rel = new DataRelation("CustToOrders", data.Tables["Customers"].Columns["customerid"], data.Tables["Orders"].Columns["customerid"]);
data.Relations.Add(rel);
Given that customerid field as a Foreign key in the Orders table so it has duplicates.
You can get the count of the duplicates this way:
data.Tables["Customers"].Columns.Add("Duplicates",
GetType(Decimal), "Count(child.customerid)");
The way I would get the results that you want would look something like this:
tempTable.Rows.Cast<DataRow>()
.Select(dr => Convert.ToString(dr[0]))
.GroupBy(dr => dr)
.Select(g => new { Title = g.Key, Count = g.Count() });
However, it's actually looping under the hood. In fact, I can't think of a way to do that kind of a grouping without inspecting each record.
The drawback is that the result of that expression is a sequence of anonymous type instances. If you still want the result to be a DataView, you could rewrite the last Select to create a new DataRow with two columns, and shove them into a new DataTable which you pass to the DataView.

Categories