Join and Update DataTable - c#

I have two Table1 and Table2 whose columns with the values are given below:
These are the two tables:
I want to update "PaidAmount" in Table1. Join based on "InvoiceNo" column in both the tables.
Table1:
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 0
2 BB 20000 10 Dec 2013 0
3 CC 10000 18 Dec 2013 0
Table2:
VoucherNo Date InvoiceNo Amount
------- ----------- ---------- ------
001 01 Nov 2013 1 5000
002 10 Nov 2013 2 6000
003 20 Nov 2013 3 7000
001 02 Nov 2013 1 2000
My desired datatable should be like this:
DesiredTable:
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 7000
2 BB 20000 10 Dec 2013 6000
3 CC 10000 18 Dec 2013 7000
How to achieve this result?
I have tried the below one.
Table1.AsEnumerable().Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
).ToList()
.ForEach(o => o.dt1_Row.SetField(4, o.dt2_Row.ItemArray[3]));
But it gives the result as
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 2000
2 BB 20000 10 Dec 2013 6000
3 CC 10000 18 Dec 2013 7000
How to get my desired table?

Your join give you a list of multiple row1, row2 couple.
So you are looping throught every couple, the first time for invoiceNo1, row1.PaidAmount = 5000, then your loop continue, and the second time, row1.PaidAmount = 2000, so your result.
You want to sum the Amount values of row2, so after joining, you have to group the datas by InvoiceValue, then perform a sum:
foreach(var grp in Table1.AsEnumerable()
.Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
)
.GroupBy(o => o.dt1_Row.ItemArray[0]))
{
var row1 = grp.First().dt1_Row;
var sum = grp.Sum(t => Convert.ToDecimal(t.dt2_Row.ItemArray[3]));
row1.SetField(4, sum)
}
For more readibility, try to avoid using ForEach of List, it doesn't really improve your code.

It looks like you're iterating through your results and overwriting as opposed to adding to the pre-existing result from other rows. You would likely want to do something like this:
Table1.AsEnumerable().Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
).ToList()
.ForEach(o => o.dt1_Row.SetField(4, o.dt1_Row.ItemArray[4] + o.dt2_Row.ItemArray[3]));

Here's one approach. You could use GroupBy to create invoice-groups and ToDictionary to create an efficient InvoiceNo to PaidAmount lookup. Then you just need a loop to update the rows:
var invoiceGroups = from r1 in Table1.AsEnumerable()
join r2 in Table2.AsEnumerable()
on r1.Field<int>("InvoiceNo") equals r2.Field<int>("InvoiceNo")
group r2 by r2.Field<int>("InvoiceNo") into InvoiceGroup
select InvoiceGroup;
Dictionary<int, int> invoiceSumLookup = invoiceGroups
.ToDictionary(ig => ig.Key, ig => ig.Sum(r => r.Field<int>("Amount")));
foreach(DataRow row in Table1.Rows)
row.SetField("PaidAmount", invoiceSumLookup[row.Field<int>("InvoiceNo")]);
Note that this does not delete duplicate InvoiceNo rows from Table1 if that's possible and not desired. It just groups the second table by that column and sums all Amounts.
It modifies the original table and does not need to create a new one.

Related

C# linq lambda expression group by multiple columns and get the first items of group

This is how my table data looks like ,
Id C1 C2 C3
-------------------------------------------------
1 John 1990 A
2 John 1990 B
3 John 2000 C
4 Mary 2001 D
5 Mary 2010 E
6 Mary 2010 F
7 Jack 2010 G
8 Jack 2010 H
9 Jack 2011 I
What I want to do is I want to group by C1,C2 columns and get the first items of each group which is order by C3 column descending.
So the result will be like
Id C1 C2 C3
-------------------------------------------------
1 John 1990 B
3 John 2000 C
4 Mary 2001 D
5 Mary 2010 F
7 Jack 2010 H
9 Jack 2011 I
And I also want to filter row number 2 to 5 using skip and take function.
So the Final result should be like
Id C1 C2 C3
-------------------------------------------------
3 John 2000 C
4 Mary 2001 D
5 Mary 2010 F
7 Jack 2010 H
What I have tried is
await data.GroupBy(d => new { d.C1, d.C2 }).Skip(1).Take(4).FirstAsync();
But it only retrun one row.
How can I solve for this ?
You need to do a Select and since there will be multiple results you'd need to use ToListAsync instead.
data.GroupBy(d => new { d.C1, d.C2 })
.Select(grp => grp.First())
.Skip(1)
.Take(4)
.ToListAsync();
You can do it by,
using System;
using System.Linq;
using System.Collections.Generic;
...
var result = await data.GroupBy(d => new { d.C1, d.C2 }) //Group by C1 and C2
.Select(s => s.First()) //Select first record from each Group
.Skip(1) //Skip first record
.Take(4) //Pick up next 4 records
.ToListAsync() //Convert 4 records to List.

Linq/Lambda complex query to get result by two different fields

Hi I have table that has following information
id StoretId itemId Mon Tue Wed
1 1 20 33 10 5
2 1 21 1 0 3
3 1 20 3 2 1
4 2 21 2 1 1
5 2 20 1 1 1
6 2 21 4 4 5
What I am trying to do is to get result by each storeId and total of items for each day by itemId, so it looks like this.
StoreId 1
itemId Mon Tue Wed
20 36 12 6
21 1 0 3
StoreId 2
itemId Mon Tue Wed
20 1 1 1
21 6 5 6
I have done so far if user inputs list of stores to get results for i.e
storelist= [{storeId :1}, {storeId: 2}, {storeId: 3}]
var results= DbContext.Set<stores>()
.Where(x=> storelist.Contains(x.storeId)
.Select(y=> new
{
storeId=y.storeId,
itemsdetails= DbContext.Set<maintable>()
.Select(m=> new
{
itemId=m.itemId.Distinct(),
Mon=m.Mon.Sum(),
Tue=m.Tue.Sum(),
Wed=m.Wed.Sum()
}
}
Query conceptually looks fine but it breaks and doesn't give result. Please let me know how to fix it to get the desired output.
Thanks
You should use GroupBy to group by store and then select the columns by the sum of the group
var result = list.GroupBy(o => o.StoreId)
.Select((o, i) => new
{
ID = o.ElementAt(i).ID,
StoreId = o.Key,
Mon = o.Sum(x => x.Mon),
Tue = o.Sum(x => x.Tue),
Wed = o.Sum(x => x.Wed),
}).ToList();
must be grouped by both StoreID and ItemID
and select sum of other values as result:
var result = from item in list
group item by new
{
item.StoreID,
item.ItemID
} into groupList
select new
{
StoreID = groupList.Key.StoreID,
ItemID = groupList.Key.ItemID,
mon = groupList.Sum(i => i.mon),
tue = groupList.Sum(i => i.tue),
wed = groupList.Sum(i => i.wed)
};

Get number of records n a group in LINQ

I wrote a LINQ query that it' result similar to this :
var res = from a in tbl
group a by {a.StateCode,a.CityCode} into grp
//where grp.Count() == 1
select ...
StateCode CityCode ......
------------------------------------------
01 001 ......
01 002 ......
02 001 ......
03 001 ......
03 002 ......
03 003 ......
indeed I want to get just 02 001 record because it has one result row in the final result.If I add commented code It returns count of records correspond to each group not count of own group.I know I can do this with two group by but is there a better way to get row that has (for example) one row in the result?
You are grouping by StateCode and CityCode so each group has only one element. You must group by StateCode only and inlcude where grp.Count() == 1 again.
Each group contains the StateCode as its key and an enumeration of the elements. Since you already filtered for groups with only a a single element you can just return the first of each group:
var res = from a in tbl
group a by a.StateCode into grp
where grp.Count() == 1
select grp.FirstOrDefault();
How about this:
(from b in Tbls
where (from a in Tbls
where a.StateCode == b.StateCode
group a by a.StateCode into grp
where grp.Count () == 1
select grp).Count() > 0
select b).Dump();
Here's the SQL I used to test it with:
create table tbl (StateCode varchar(5), CityCode varchar(5))
insert tbl values ('01', '001')
insert tbl values ('01', '002')
insert tbl values ('02', '001')
insert tbl values ('03', '001')
insert tbl values ('03', '002')
insert tbl values ('03', '003')

Split a DataTable into 2 or more DataTables based on Column value

I have a DataTable called "DTHead" which has the following records,
MIVID Quantity Value
------ ---------- --------
1 10 3000
1 20 3500
1 15 2000
2 20 3000
2 50 7500
3 25 2000
Here, I need to split the above DataTable into three tables based on the MIVID such as follows;
DTChild1:
MIVID Quantity Value
------- ---------- ---------
1 10 3000
1 20 3500
1 15 2000
DTChild2:
MIVID Quantity Value
------- ---------- ---------
2 20 3000
2 50 7500
DTChild3:
MIVID Quantity Value
------- ---------- ---------
3 25 2000
Suppose, if the Header DataTable contains 4 different MIVID means, then 4 Child DataTable should be created based on the MIVID. How to do this?
Use LINQ to DataTable to group the first column by GroupBy, and use method CopyToDataTable to copy list of rows to DataTable
List<DataTable> result = DTHead.AsEnumerable()
.GroupBy(row => row.Field<int>("MIVID"))
.Select(g => g.CopyToDataTable())
.ToList();
Then you can get the result as a list of DataTables as you expected.
DataTable tbl = new DataTable("Data").AsEnumerable()
.Where(r => r.Field<int>("ParentId") == 1) // ParentId == 1
.Where(r => r.Field<int>("Id") > 3) // Id > 3
.Where(r => r.Field<string>("Name").Contains("L")) // Name contains L
.OrderBy(r => r.Field<int>("Id")) // Order by Id
.CopyToDataTable();

linq group by, order by

I have the following list
ID Counter SrvID FirstName
-- ------ ----- ---------
1 34 66M James
5 34 66M Keith
3 55 45Q Jason
2 45 75W Mike
4 33 77U Will
What I like to do is to order by ID by ascending and then
get the first value of Counter, SrvID which are identical (if any).
So the output would be something like:
ID Counter SrvID FirstName
-- ------ ----- ---------
1 34 66M James
2 45 75W Mike
3 55 45Q Jason
4 33 77U Will
Note how ID of 5 is removed from the list as Counter and SrvID was identical to what I had for ID 1 but as ID 1 came first
I removed 5.
This is what I would do but not working
var result = (from ls in list1
group ts by new {ls.Counter, ls.SrvID}
order by ls.ID
select new{
ls.ID,
ls.Counter.FirstOrDefault(),
ls.SrvID.First,
ls.FirstName}).ToList()
list1.GroupBy(item => new { Counter = item.Counter, SrvID = item.SrvID })
.Select(group => new {
ID = group.First().ID,
Counter = group.Key.Counter,
SrvID = group.Key.SrvID,
FirstName = group.First().FirstName})
.OrderBy(item => item.ID);
Group the records up, and pick a winner from each group.
var query =
from record in list1
group record by new {record.Counter, record.SrvID } into g
let winner =
(
from groupedItem in g
order by groupedItem.ID
select groupedItem
).First()
select winner;
var otherQuery = list1
.GroupBy(record => new {record.Counter, record.SrvID })
.Select(g => g.OrderBy(record => record.ID).First());

Categories