Compare each row of two identical Datatables for changed values C# - c#

I need to compare two datatables which are having same schema and move the differences into another datatable. Below is my code which is not working fine:
DataTable dt1 = new DataTable("TableChanged");
dt1.Columns.Add("StateID",typeof(int));
dt1.Columns.Add("StateInitial");
dt1.Columns.Add("IsActive");
dt1.Rows.Add(new object[] { 10, "GA", 1 });
dt1.Rows.Add(new object[] { 11, "HI", 0 });
dt1.Rows.Add(new object[] { 12, "ID", 1 });
dt1.Rows.Add(new object[] { 13, "IL", 1 });
dt1.Rows.Add(new object[] { 14, "IN", 0 });
dt1.Rows.Add(new object[] { 15, "IA", 1 });
dt1.Rows.Add(new object[] { 23, "MN", 0 });
DataTable dt2 = new DataTable("TableOriginal");
dt2.Columns.Add("StateID", typeof(int));
dt2.Columns.Add("StateInitial");
dt2.Columns.Add("IsActive");
dt2.Rows.Add(new object[] { 10, "GA", 1 });
dt2.Rows.Add(new object[] { 11, "HI", 1 });
dt2.Rows.Add(new object[] { 12, "ID", 1 });
dt2.Rows.Add(new object[] { 13, "IL", 0 });
dt2.Rows.Add(new object[] { 14, "IN", 1 });
dt2.Rows.Add(new object[] { 15, "IA", 1 });
dt2.Rows.Add(new object[] { 23, "MN", 1 });
var matched = from table1 in dt1.AsEnumerable()
join table2 in dt2.AsEnumerable() on table1.Field<int>("StateID") equals table2.Field<int>("StateID")
//where table1.Field<object>("IsActive") == "0"
where table1.Field<string>("StateInitial") == table2.Field<string>("StateInitial") || table1.Field<object>("IsActive") == table2.Field<object>("IsActive")
select table1;
var missing = from table1 in dt1.AsEnumerable()
where !matched.Contains(table1)
select table1;
After comparison, I want result like:
StateID|StateInitial|IsActive
11 "HI" 0
13 "IL" 1
14 "IN" 0
23 "MN" 0

You can use DataRowComparer.Default, which compares every field of a DataRow, for the LINQ methods Intersect and Except. The latter gives you the missing rows, your desired result:
DataRowComparer<DataRow> fieldComparer = DataRowComparer.Default;
IEnumerable<DataRow> matched = dt1.AsEnumerable().Intersect(dt2.AsEnumerable(), fieldComparer);
IEnumerable<DataRow> missing = dt1.AsEnumerable().Except(dt2.AsEnumerable(), fieldComparer);
If you want to add the missing rows into a third table, you could use:
DataTable result = missing.CopyToDataTable();
But i suggest to use a different way because that throws an exception if there were no missing rows:
DataTable result = dt1.Clone(); // empty, same schema
foreach(DataRow row in missing)
result.ImportRow(row);

Related

Removing Primary Key and Joining Rows in Datatable

I'm trying to sort a DataTable to be able to get the best selling items by order in C#. The DataTable's fields are orderId, productId, size, and amount (amount = how much of it was bought). The current primary key in the table is comprised of orderId, productId, and size. I would like to get the best selling items by sorting the table in a way that would remove orderId, and have productId and size as the new primary key, and amount as the sum of all amount's for orders with this productId and size.
What would be the best way to do this? I'm attaching my current table data for reference. My table data for reference.
If anything is unclear please let me know, this is my first time using Stack Overflow.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication20
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("orderid", typeof(int));
dt.Columns.Add("productid", typeof(int));
dt.Columns.Add("size", typeof(int));
dt.Columns.Add("amount", typeof(int));
dt.Rows.Add(new object[] { 1, 1, 38, 12 });
dt.Rows.Add(new object[] { 1, 1, 41, 6 });
dt.Rows.Add(new object[] { 1, 2, 36, 8 });
dt.Rows.Add(new object[] { 1, 2, 38, 5 });
dt.Rows.Add(new object[] { 1, 3, 46, 2 });
dt.Rows.Add(new object[] { 4, 3, 40, 7 });
dt.Rows.Add(new object[] { 8, 3, 40, 7 });
dt.Rows.Add(new object[] { 9, 2, 40, 9 });
dt.Rows.Add(new object[] { 12, 2, 37, 5 });
dt.Rows.Add(new object[] { 13, 2, 37, 4 });
dt.Rows.Add(new object[] { 14, 2, 38, 3 });
dt.Rows.Add(new object[] { 15, 3, 41, 4 });
dt.Rows.Add(new object[] { 16, 2, 36, 7 });
dt.Rows.Add(new object[] { 16, 3, 41, 5 });
dt.Rows.Add(new object[] { 17, 2, 38, 4 });
dt.Rows.Add(new object[] { 18, 3, 40, 3 });
dt.Rows.Add(new object[] { 19, 5, 38, 9 });
dt.Rows.Add(new object[] { 20, 2, 36, 2 });
dt.Rows.Add(new object[] { 21, 1, 40, 3 });
dt.Rows.Add(new object[] { 22, 1, 38, 8 });
dt.Rows.Add(new object[] { 23, 1, 40, 9 });
dt.Rows.Add(new object[] { 24, 2, 37, 1 });
dt.Rows.Add(new object[] { 25, 5, 39, 4 });
dt.Rows.Add(new object[] { 2034, 3, 40, 3 });
dt.Rows.Add(new object[] { 2035, 2, 37, 6 });
dt.Rows.Add(new object[] { 2035, 3, 40, 5 });
dt.Rows.Add(new object[] { 2036, 2, 36, 2 });
dt.Rows.Add(new object[] { 2037, 2, 37, 3 });
dt.Rows.Add(new object[] { 2037, 3, 41, 7 });
dt.Rows.Add(new object[] { 2038, 1, 39, 3 });
dt.Rows.Add(new object[] { 2038, 5, 37, 4 });
var results = dt.AsEnumerable()
.GroupBy(x => new { productid = x.Field<int>("productid"), size = x.Field<int>("size") })
.Select(x => new { productid = x.Key.productid, size = x.Key.size, count = x.Count(), total = x.Sum(y => y.Field<int>("amount")) })
.OrderByDescending(x => x.count)
.ToList();
}
}
}

C# SQL Query to return records and related records

I have a table as follows
I have a query that runs each day to find anything that's 5 days old or greater and has a status of "In Progress" and set the status to "Declined".
What I need to be able to do is also set the status to "Declined" for anything with a Linked_ID (child) where the ID (Parent) has been changed.
So in the table I already set ID 1 to "Declined" but I also need to set ID's 2,3,4,8 and 10 to "Declined"
Below is what I have so far, any support would be amazing and really helpful
var Process = from a in db.Table1
where a.Status == "In Progress" && a.Date_Created = DateTime.Now.AddDays(-5)
select a;
foreach (Table1 a in Process)
{
a.Status = "Declined";
}
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Declined declined = new Declined();
declined.Query(DateTime.Now.AddDays(1));
}
}
public class Declined
{
DataTable dt;
public Declined()
{
dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Status", typeof(string));
dt.Columns.Add("Date Created", typeof(DateTime));
dt.Columns.Add("Link_ID", typeof(int));
dt.Rows.Add(new object[] { 1, "In Progress", new DateTime(2020, 9, 25) });
dt.Rows.Add(new object[] { 2, "Completed", new DateTime(2020, 9, 28),1 });
dt.Rows.Add(new object[] { 3, "In Progress", new DateTime(2020, 9, 28),1 });
dt.Rows.Add(new object[] { 4, "In Progress", new DateTime(2020, 9, 28),1 });
dt.Rows.Add(new object[] { 5, "In Progress", new DateTime(2020, 9, 28),2 });
dt.Rows.Add(new object[] { 6, "In Progress", new DateTime(2020, 9, 28),3 });
dt.Rows.Add(new object[] { 7, "In Progress", new DateTime(2020, 9, 28) });
dt.Rows.Add(new object[] { 8, "In Progress", new DateTime(2020, 9, 28),1 });
dt.Rows.Add(new object[] { 9, "In Progress", new DateTime(2020, 9, 28) });
dt.Rows.Add(new object[] { 10, "In Progress", new DateTime(2020, 9, 28),1 });
}
public void Query(DateTime date)
{
List<DataRow> rows = dt.AsEnumerable().Where(x => (x.Field<string>("Status") == "In Progress") && (date.Date.Subtract(x.Field<DateTime>("Date Created")).Days > 5)).ToList();
foreach(DataRow row in rows)
{
if (rows.Count > 0) ChangeChildren_Recursive(row, row.Field<DateTime>("Date Created"));
}
}
public void ChangeChildren_Recursive(DataRow row, DateTime date)
{
int id = row.Field<int>("ID");
DateTime rowDate = row.Field<DateTime>("Date Created").Date;
string status = row.Field<string>("Status");
if((status == "In Progress") && (date != rowDate)) row["Status"] = "Declined";
List<DataRow> children = dt.AsEnumerable().Where(x => (x.Field<int?>("Link_ID") == id) && (x.Field<int?>("Link_ID") != null)).ToList();
foreach (DataRow childRow in children)
{
ChangeChildren_Recursive(childRow, date);
}
}
}
}

Sum Datatable values with same criteria

I have a data table and I need to merge the cells with the same values in the columns IPC and Second Best Issuer and sum the values in the columns Dirty value PC and Par Value LC. All the values are type string.
For example:
I'm a beginner with query and I'm looking for simple way to do this in C# with LINQ.
Thank You
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication11
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("IPC", typeof(int));
dt.Columns.Add("Dirty", typeof(int));
dt.Columns.Add("Second", typeof(string));
dt.Columns.Add("Par", typeof(int));
dt.Rows.Add(new object[] { 1, 5, "BB", 55 });
dt.Rows.Add(new object[] { 1, 4, "B", 30 });
dt.Rows.Add(new object[] { 2, 15, "AAA", 20 });
dt.Rows.Add(new object[] { 1, 10, "BB", 80 });
dt.Rows.Add(new object[] { 2, 20, "AA", 90 });
dt.Rows.Add(new object[] { 2, 30, "AAA", 50 });
dt.Rows.Add(new object[] { 1, 5, "B", 60 });
dt.Rows.Add(new object[] { 2, 15, "AA", 70 });
var groups = dt.AsEnumerable().GroupBy(x => new {ipc = x.Field<int>("IPC"), second = x.Field<string>("Second")}).ToList();
DataTable dtsum = dt.Clone();
foreach(var group in groups)
{
dtsum.Rows.Add(new object[] {group.Key.ipc, group.Sum(y => y.Field<int>("Dirty")), group.Key.second, group.Sum(y => y.Field<int>("Par"))});
}
}
}
}

Left Outer Join with multiple Data tables

I have 3 DataTables
DataTable1
Id Version URL Owner
1 1 "xx" "alice"
2 1 "yy" "bob"
3 1 "zz" "Mike"
4 1 "ww" "Rob"
5 1 "ww" "Bick"
DataTable2
Id Version DomainID Region Type
1 1 aa asia 1
2 1 bb europe 2
3 1 cc africa 1
4 1 dd aus1 0
DataTable3
Id Size FreeSpace
aa 2500 2000
bb 3300 3000
cc 5500 50
Expected Join
Id Version URL Owner DomainID Region Type Size Freespace
1 1 "xx" "alice" aa asia 1 2500 2000
2 1 "yy" "bob" bb europe 2 3300 3000
3 1 "zz" "Mike" cc africa 1 5500 50
4 1 "ww" "sean" dd aus1 0 null null
5 1 "ww" "Bick" null null null null null
I am doing a Join Operation on these tables using Linq as follows:
// Datatable1 joins with Datatable2 on Id and version (datatable1) --> Id and version (datatable2)
// Datatable2 joins with Datatable3 on DomainId(datatable2) --> Id(datatable3)
var result = from dataRows1 in DataTable1.AsEnumerable()
join dataRows2 in DataTable2.AsEnumerable() on
new
{
Id = dataRows1.Field<long>("Id"),
Version = dataRows1.Field<long>("version")
} equals new
{
Id = dataRows2.Field<long>("Id"),
Version = dataRows2.Field<long>("version")
}
into tempJoin
from datarowc in tempJoin.DefaultIfEmpty()
join dataRows3 in DataTable3.AsEnumerable() on
dataRowsc.Field<long>("DomainId") equals dataRows3.Field<long>("Id")
select new
{
datarow1,
datarowc,
datarow3
}
I am getting an exception of datarowc to be null.
Not quite sure why datarowc is null here and how to achieve the expected join.
using System.Data;
using System.Linq;
namespace CodeWars
{
class Program
{
static void Main(string[] args)
{
var result = datarows1.AsEnumerable()
.Select(x => new
{
Tab1Row = x,
Tab2Row = datarows2.AsEnumerable().FirstOrDefault(
y => x.Field<int>("Id") == y.Field<int>("Id") &&
x.Field<int>("Version") == y.Field<int>("Version")
)
}
)
.Select(x => new
{
Tab1Row = x.Tab1Row,
Tab2Row = x.Tab2Row,
Tab3Row = datarows3.AsEnumerable().FirstOrDefault(
y => x?.Tab2Row?.Field<string>("DomainId") == y.Field<string>("Id")
)
}
);
}
static DataTable datarows1 = new DataTable
{
Columns = {
{ "Id", typeof(int) },
{ "Version", typeof(int) },
{ "URL", typeof(string) },
{ "Owner", typeof(string) },
},
Rows = {
{ 1, 1, "xx", "alice" },
{ 2, 1, "yy", "bob" },
{ 3, 1, "vv", "mike" },
{ 4, 1, "ww", "rob" },
{ 5, 1, "zz", "bick" },
}
};
static DataTable datarows2 = new DataTable
{
Columns = {
{ "Id", typeof(int) },
{ "Version", typeof(int) },
{ "DomainID", typeof(string) },
{ "Region", typeof(string) },
{ "Type", typeof(int) },
},
Rows = {
{ 1, 1, "aa", "asia", 1 },
{ 2, 1, "bb", "europe", 2},
{ 3, 1, "cc", "asia", 1},
{ 4, 1, "dd", "aus1", 0},
}
};
static DataTable datarows3 = new DataTable
{
Columns = {
{ "Id", typeof(string) },
{ "Size", typeof(int) },
{ "FreeSpace", typeof(int) },
},
Rows = {
{ "aa", 2500, 2000 },
{ "bb", 3300, 3000 },
{ "cc",5500, 50},
}
};
}
}
.Join() performs inner join, but you want left outer join, so forget about .Join()
Code I've provided gives you the result you expect. But maybe you need to add one more Select to form datastructure you need.

Group a List<object[]>

I have a question that's similar to yesterday's question.
I've got this List<object[]>
List<object[]> olst = new List<object[]>();
olst.Add(new object[] { "AA1", "X", 1, 3.50 });
olst.Add(new object[] { "AA2", "Y", 2, 5.20 });
olst.Add(new object[] { "AA2", "Y", 1, 3.50 });
olst.Add(new object[] { "AA1", "X", 1, 3.20 });
olst.Add(new object[] { "AA1", "Y", 2, 5.30 });
I need to produce List<object[]> to hold this:
"AA1", "X", 2, 6.70
"AA2", "Y", 3, 8.70
"AA1", "Y", 2, 5.30
In other words, I need to group olst by the 1st and 2nd elements of each object[] and sum 3rd and 4th.
I could use a for loop, but I was hoping someone could help me using lambda expressions and/or linq to accomplish this.
List<object[]> olst = new List<object[]>();
olst.Add(new object[] { "AA1", "X" });
olst.Add(new object[] { "AA2", "Y" });
olst.Add(new object[] { "AA2", "Y" });
olst.Add(new object[] { "AA1", "X" });
olst.Add(new object[] { "AA1", "Y" });
var result = from ol in olst
group ol by new {p1 = ol[0], p2 = ol[1]}
into g
select g.First();
Something like this?
You need to group by an anonymous type, then sum the third and fourth columns:
List<object[]> grouped = olst
.GroupBy(o => new { Prop1 = o[0].ToString(), Prop2 = o[1].ToString() })
.Select(o => new object[]
{
o.Key.Prop1,
o.Key.Prop2,
o.Sum(x => (int)x[2]),
o.Sum(x => (double)x[3])
})
.ToList();
As suggested in the comments, I would go with Tuple and maybe use HashSet instead, since it will only append if item doesn't exist (also, it is fast). If you add items to hashset, there's no need to select distinct as long as your type provides necessary Equals and GetHashCode methods.
Something like this:
var olst = new HashSet<Tuple<string,string>>();
olst.Add(Tuple.Create("AA1", "X"));
olst.Add(Tuple.Create("AA1", "X"));
olst.Add(Tuple.Create("AA2", "Y"));
olst.Add(Tuple.Create("AA2", "Y"));
If you need you can convert it to list. Here's an example:
olst.ToList().ForEach(x=> Console.WriteLine(x.Item1 + " " + x.Item2));
Will print out
AA1 X
AA2 Y

Categories

Resources