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.
Related
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 Merge rows data From DataTable in C#?
I think that this is a good use case for the LINQ Group clause. You can start with something like this:
var rowGroups = dataTable.Rows.GroupBy(row =>
new {RecptNo = row["recpt_no"], Test = row["Test"]});
foreach(var group in rowGroups)
{
//Here "group" is a collection of rows with the same rectp_no and test. Process as required.
//You could also check group.Key.RecptNo and group.Key.Test here if necessary.
}
Here's a link of that relates to your question. Hope it helps.
How to merge rows in a DataTable when data in multiple columns match?
myDataTable has a column named ORDER_NO. I would like to select the rows from this table which appears once. If a value appears two times than it should not selected.
ORDER_NO contain Values
1000A
1001A
1001B
1002A
1002B
1002C
1000A
1001A
1001B
I want to select only form the values above are:
1002A
1002B
1002C
as they appears once in the column. Can anyone help?
So you want only unique rows according to the ORDER_NO column?
Presuming that it's a string column you could use LINQ's Enumerable.GroupBy:
var uniqueRows = table.AsEnumerable()
.GroupBy(row => row.Field<string>("ORDER_NO"))
.Where(group => group.Count() == 1)
.Select(group => group.First());
if you want a new DataTable from the unique rows you can use:
table = uniqueRows.CopyToDataTable();
If you instead only want this column's values:
IEnumerable<string> unqiueOrderNumbers = uniqueRows.Select(row => row.Field<string>("ORDER_NO"));
Apart from #Tim's answer you can also use DataView to simplify this thing
DataView view = new DataView(table);
DataTable distinctValues = view.ToTable(true, "ORDER_NO");
I think this is the simplest way to get distinct values from any table. You can even mention multiple columns in the ToTable method. Just pass column name as argument like ORDER_NO is send in above sample code.
I have one DataTable
Datatable dt;
There are 10 columns there, including the ID of the row.
And in the view state I have a generic list like:
List<MyObject>;
Myobject has some fields including the same ID.
The datatable has ALL items, and the list has a reduced list of those items.
However my gridview needs to be bound to a datatable, not to a List
The question is how can I get into one new Datatable (with same columns as the first one) with all items from the list
You could use Linq:
var rowsInList = from row in dt.AsEnumerable()
join obj in objectList
on row.Field<int>("ID") equals obj.ID
select row;
DataTable filtered = rowsInList.CopyToDataTable();
Enumerable.Join is efficient since it's using a set. Due to LINQ's deferred execution rowsInList is just a "no-op"-query which gets executed at CopyToDataTable.
Another, less efficient, approach is using Where + Any (here with method syntax):
DataTable filtered = dt.AsEnumerable()
.Where(row => objectList.Any(o => row.Field<int>("ID") == o.ID))
.CopyToDataTable();
I have a datatable returned as a result of fetching data from a spreadsheet. I need to display the resultset only with the distinct rows depends up on only a column.
For example I have a datatable with columns
id | name | age | email
Then if more than one record with the same id is listed it should omitted. I tried
dt = dt.DefaultView.ToTable(true)
but it returns the distinct records with respect to all columns. I need the distinct records only based on the id.
Can anyone help me on this?
You can use GroupBy :-
DataTable result = dt.AsEnumerable()
.GroupBy(x => x.Field<int>("Id"))
.Select(x => x.First()).CopyToDataTable();
Please note, in case of a matching Id, I am taking the first record and ignoring the rest.
You need to mention the column name on which the ToTable operation will execute to select distinct values.
Please find below the code section
DataView view = new DataView(table);
DataTable distinctValues = view.ToTable(true, "id");