I have a datatable in which multiple data is populated. Need to check a column which contains same values in different rows and add up all the similar rows into one in the same datatable.
for example
id pid pname pAmountex vat vat
1 4 t1 123 2
2 3 t2 45 3
3 4 t3 56 7
4 3 t4 23 8
in the above table,pid column has similar values 4& 3 .i need to sum up the pamountex,vat column of 1st and 3 rd rows for pid 4 and sum up 2 and 3 rows for pid 3.
Here is an example with LINQ:
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("pid");
dt.Columns.Add("pname");
dt.Columns.Add("pamountex",typeof(int));
dt.Columns.Add("vat",typeof(int));
dt.Rows.Add( new object[] { 1, 4, "t1", 123, 2 } );
dt.Rows.Add( new object[] { 2, 3, "t2", 45, 3 } );
dt.Rows.Add( new object[] { 3, 4, "t3", 56, 7 } );
dt.Rows.Add( new object[] { 4, 3, "t4", 23, 8 } );
dt.AsEnumerable()
.GroupBy(r => r.Field<string>("pid") )
.Where (r => r.Count() > 1)
.Select (gr =>
new { id = 0,
pid = gr.Key,
pname = "???",
pamountex = gr.Sum (g => g.Field<int>("pamountex")),
vat = gr.Sum (g => g.Field<int>("vat"))
})
.ToList()
.ForEach( r => dt.Rows.Add(
new object[] {
r.id,
r.pid,
r.pname,
r.pamountex,
r.vat } ));
You did not specify what values are required in id and pname columns for rows that keep sum so I added some defaults(id = 0 and pname = "???") - change it according to your needs.
I assumed that rows with source data should stay in the table.
Related
I'm writing a method that selects customer records based on a group by with the most recent purchase total equal (passed to the method).
I did some search and nothing showed up that helps me to achieve the desired output.
customerList.GroupBy(x=> x.CustomerID },
(key, result) =>
{
var orderedList = result.OrderByDescending(c => c.Date).ToList();
return new Customer
{
CustomerID = orderedList.First().CustomerID,
PurchaseID = orderedList.First().PurchaseID,
Price = orderedList.First().Price,
};
});
CUSTOMERID
PURCHACEID
PRICE
DATE
1
11
235
01-03-2021
1
12
230
01-03-2021
1
14
235
05-04-2021
1
15
535
06-04-2021
1
16
230
07-04-2021
If I'm looking for most recent total purchase price of 1000, I should get
CUSTOMERID
PURCHACEID
PRICE
DATE
1
14
235
05-04-2021
1
15
535
06-04-2021
1
16
230
07-04-2021
You probably need to produce a separate list with the cumulative sums. You can then use TakeWhile to take items until some condition is reached
var list = new[] { 1, 2, 3, 4, 5 };
var sum = 0;
var cummulativeSums = list.Select(v => sum += v).ToList();
var result= list.TakeWhile((_, index) => cummulativeSums[index] < 7).ToList();
// 1, 2, 3
Or you could do it all in one go by creating intermediate pair of values.
var list = new[] { 1, 2, 3, 4, 5 };
var sum = 0;
var result = list.Select(value => (sum += value, value))
.TakeWhile(pair => pair.Item1 < 7)
.Select(pair => pair.value)
.ToList();
So here is the issue I am facing. I have a one to many list relationship.
Here are examples of how the lists would look.
The objects in list 1 are dynamic and have a few properties with the most important one being "id".
The second list has a defined object looking something like this "id" "value desc" "actual value".
The second list can have many rows belonging to the first list. The "value desc" is the property name and the "actual value".
I need to combine these lists into something that looks like this. An object with all list 1 properties and all corresponding rows in the second list. If the second list had 3 items belonging to an item in list 1 then the new object should have all properties of list 1 along with all the rows gathered from list 2 in a flat structure like way.
Data examples
Table 1:
id
name
1
bob
2
joe
Table 2
id
propname
value
1
length
2
1
age
12
1
haircolor
blue
2
length
5
2
age
90
2
haircolor
red
How I want the data to look
id
name
length
haircolor
age
1
bob
2
blue
12
2
joe
5
red
90
Currently, I have this working.
public IEnumerable<dynamic> test(List<dynamic> data, List<modal>
dataset)//closest
{
var query = (from a in data
join b in dataset
on a.id equals b.id into t
select new {
a,
t
});
return query;
}
However, the result is an object with properties of list 1 and then a property on that object that is an array of items found in list 2. I need these items to not be in an array and be property names with values on the new created object.
I hope my explanation was clear enough!
Use a pivolt table :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("id", typeof(int));
dt1.Columns.Add("name", typeof(string));
dt1.Rows.Add(new object[] { 1, "bob" });
dt1.Rows.Add(new object[] { 2, "bob" });
DataTable dt2 = new DataTable();
dt2.Columns.Add("id", typeof(int));
dt2.Columns.Add("propname", typeof(string));
dt2.Columns.Add("value", typeof(object));
dt2.Rows.Add(new object[] { 1, "length", 2 });
dt2.Rows.Add(new object[] { 1, "age", 12 });
dt2.Rows.Add(new object[] { 1, "haircolor", "blue"});
dt2.Rows.Add(new object[] { 2, "length", 5 });
dt2.Rows.Add(new object[] { 2, "age", 90 });
dt2.Rows.Add(new object[] { 2, "haircolor", "red" });
DataTable pivot = new DataTable();
string[] properties = dt2.AsEnumerable().Select(x => x.Field<string>("propname")).Distinct().OrderBy(x => x).ToArray();
pivot.Columns.Add("id", typeof(int));
pivot.Columns.Add("name", typeof(string));
DataColumn[] columns = properties.Select(x => new DataColumn(x, typeof(object))).ToArray();
pivot.Columns.AddRange(columns);
var joins = from id1 in dt1.AsEnumerable()
join id2 in dt2.AsEnumerable() on id1.Field<int>("id") equals id2.Field<int>("id")
select new { id = id1.Field<int>("id"), name = id1.Field<string>("name"), id2 = id2 };
var groups = joins.GroupBy(x => x.id);
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["id"] = group.Key;
newRow["name"] = group.First().name;
foreach (var row in group)
{
newRow[row.id2.Field<string>("propname")] = row.id2.Field<object>("value");
}
}
}
}
}
I am using ASP.NET MVC and have a many-to-many table that as follows:
custID | objID
================
1 2
1 3
2 5
2 2
3 2
Both userID and objID are Foreign Keys linking to other tables. What I would like to do is get the highest count based on objID. The table above will yield the following results:
objID | objName | Price
=======================
2 | Chicken | 10
The custID does not matter in this scenario as I just want to get the objID with the highest count.
I've tried the following but i'm stuck here:
//retrieve many-to-many table
var retrieved = db.Customer.Include(c => c.Objects)
var topID = retrieved.GroupBy(q => q.objID)
.OrderByDescending(g => g.Count())
Should do the trick.
var topID = retrieved.GroupBy(q => q.objID)
.Select(g => new { Id = g.Key, Total = g.Count() })
.OrderByDescending(g => g.Total).First().Id;
List<int> lst = new List<int>() { 1, 3, 4, 5, 1, 2, 3, 4, 5, 2, 3, 2 };
var count = lst.GroupBy(q => q).OrderByDescending(s => s.Count())
.First().Key;
var lstKeyWithCount lst.GroupBy(i => i).Select(g => new { Key=g.Key,Count=g.Count() });
in lstKeyWithCount variable you get key with count.
in count variable you get most times repeated value.
I am reading an excel using .NET and need to perform certain checks on that excel.
The excel example is as below:
ColumnA ColumnB ColumnC
1001 Null 10
1001 W101 5
1001 W102 4
1001 W103 2
1002 Null 12
1002 W104 5
1002 W105 3
1003 W106 5
1003 W107 2
The requirement is as follows:
If there is a Null entry in ColumnB, then I need to compare the values under ColumnC,i.e. the sum of the values(5,4,2 as against the values W101, W102, W103) should be equal to 10(value against Null), if not then write an error in a log file.
My problem is that the there can be n values in ColumnC, how to loop against it.
In the above excel, the values corresponding to 1001 (ColumnA) are 4 in number whereas, the values corresponding to 1002, 1003 (ColumnA) are 3 and 2 in number respectively.
How to write a generic logic for it, I am not able to understand.
Below is the code which I have written, but it will work only when I have 4 values corresponding to 1001 in ColumnA.
FileStream file = File.Open(path, FileMode.Open, FileAccess.Read);
IExcelDataReader obj = ExcelReaderFactory.CreateOpenXmlReader(file);//uses a 3rd party library
obj.IsFirstRowAsColumnNames = true;
DataSet ds = obj.AsDataSet();
DataTable dt = ds.Tables[0];
for (int i = 0; i < dt.Rows.Count; i++)
{
if (dt.Rows[i][1].ToString() == "Null")
{
double a = (double)dt.Rows[i][2];
double x = (double)dt.Rows[i + 1][2];
double y = (double)dt.Rows[i + 2][2];
double z = (double)dt.Rows[i + 3][2];
if (a != (x+ y + z))
{
Response.Write("Mismatch in row: " + dt.Rows[i + 1][1]);
Response.Write("<br/>");
}
}
}
Also, I have used a 3rd party library to read the excel and convert it into a DataSet.
The following query will get the results you need:
Test data used:
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("A", typeof(string)));
dt.Columns.Add(new DataColumn("B", typeof(string)));
dt.Columns.Add(new DataColumn("C", typeof(int)));
dt.Rows.Add(new object[] { "1001", "Null", 10 });
dt.Rows.Add(new object[] { "1001", "W101", 5 });
dt.Rows.Add(new object[] { "1001", "W102", 4 });
dt.Rows.Add(new object[] { "1001", "W103", 1 });
dt.Rows.Add(new object[] { "1002", "Null", 12 });
dt.Rows.Add(new object[] { "1002", "W104", 5 });
dt.Rows.Add(new object[] { "1002", "W105", 3 });
dt.Rows.Add(new object[] { "1003", "W106", 5 });
dt.Rows.Add(new object[] { "1003", "W107", 2 });
LINQ:
var result =
dt
.AsEnumerable()
// Group by the A-column.
.GroupBy(r => (string)r["A"])
// Get only those groups where the first item in the B-column is 'Null':
.Where(g => g.FirstOrDefault(r => (string)r["B"] == "Null") != null)
// Get only those groups where the sum of the items of the C-column
// after the first one is different from the first item:
.Where(g => (int)g.First()["C"] != g.Skip(1).Sum(r => (int)r["C"]))
.ToList();
The result will contain rows groupped by the A-column that have invalid sums. If you are not interested in the actaul rows but only if they are valid then put .Any() in place of .ToList().
If you don't use a "Null" strings but a real null like:
dt.Rows.Add(new object[] { "1001", null, 10 });
you'll need to exchange the first Where to:
.Where(g => g.FirstOrDefault(r => r.Field<string>("B") == null) != null)
I have following Table with records for ActivityLog:
ID, UserId, Category, Created
1 11 DeAssigned 05/10/2012
2 11 LogIn 05/11/2012
3 20 Assigned 06/15/2012
4 11 Assigned 06/10/2012
5 20 DeAssigned 06/13/2012
6 20 Assigned 07/12/2012
7 11 DeAssigned 07/16/2012
8 20 Assigned 08/15/2012
...
now i want to query the Table to create same struct the same Result such as this:
var data = new[] {
new { Month = "05", Assigned = 14, DeAssigned = 5, LogIn=1 },
new { Month = "06", Assigned = 5, DeAssigned = 2, LogIn=0 },
new { Month = "07", Assigned = 50, DeAssigned = 8, LogIn=0 },
new { Month = "08", Assigned = 15, DeAssigned = 1, LogIn=0 }
};
what i have achieved:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by new { l.Created.Value.Month, l.Category }
into groups
orderby groups.Key.Month
select new
{
Month = groups.Key.Month,
Category = groups.Key.Category,
Count = groups.Count()
});
there is not optimal result but count all Activity's grouped by Month:
[0] {Month = 6, Category = Assigned, Count = 2}
[0] {Month = 6, Category = Designed, Count = 1}
[0] {Month = 6, Category = LogIn, Count = 1}
[0] {Month = 7, Category = Assigned, Count = 3}
How can i query my Table to Format my Result in "Horizontal Counted" format?
Or simplier:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by l.Created.Month into groups
orderby groups.Key ascending
select new
{
Month = groups.Key,
Assigned = groups.Where(g => g.Category == "Assigned").Count(),
Deassigned = groups.Where(g => g.Category == "DeAssigned").Count(),
LogIn = groups.Where(g => g.Category == "LogIn").Count()
});