LINQ only sum rows if columns totaled is not zero - c#

Given the following rows:
Amount, Name, Id
Scenario 1: 100.00,ABC,101
-100.00,ABC,101
Scenario 2: 50.00,XYZ,123
-100.00,XYZ,123
I want to sum and group the rows only if the the amount does not totaled to 0.00 amount. So the Linq query should return this:
Amount, Name, Id
Scenario 1: 100.00,ABC,101
-100.00,ABC,101
Scenario 2:-50.00,XYZ,123
What I have so far:
var results = dt.AsEnumerable().GroupBy(row => new
{
Name = row.Field<string>("NAME"),
Id = row.Field<int>("ID")
}).Select(grp =>
{
DataRow dr = dt.NewRow();
dr["AMOUNT"] = grp.Sum(r => r.Field<decimal>("AMOUNT"));
dr["NAME"] = grp.Key.Name;
dr["ID"] = grp.Key.Id;
return dr;
}).CopyToDataTable();

You could try the following query using SelectMany extension method:
var query= dt.AsEnumerable().GroupBy(row => new
{
Name = row.Field<string>("NAME"),
Id = row.Field<int>("ID")
})
.SelectMany(grp=>
{
var sum=grp.Sum(r => r.Field<decimal>("AMOUNT");
if(sum!=0)
{
DataRow dr = dt.NewRow();
dr["AMOUNT"] = sum;
dr["NAME"] = grp.Key.Name;
dr["ID"] = grp.Key.Id;
return dr;
}
else
{
return grp;
}
}).CopyToDataTable();

It's difficult to understand what you're asking, so I'm assuming that you mean:
Sum and group rows, so only a single summarised transaction is listed for any given ID, unless the total is zero, then list all of the transactions for that ID.
Here's a working example, with the test data you provided:
var amounts = new[]
{
new
{
Amount = 100.00m,
Name = "ABC",
Id = 101,
},
new
{
Amount = -100.00m,
Name = "ABC",
Id = 101,
},
new
{
Amount = 50.00m,
Name = "XYZ",
Id = 123,
},
new
{
Amount = -100.00m,
Name = "XYZ",
Id = 123,
},
};
// summarise everything
var summaries = from a in amounts
group a by new { a.Id, a.Name } into grouping
select new
{
Amount = grouping.Sum(g => g.Amount),
grouping.Key.Name,
grouping.Key.Id,
};
// get the ids of records we need the full audit log for
var zeroSummaries = summaries.Where(s => s.Amount == 0).Select(s => s.Id).ToList();
// concat the summarised records together with the ones we need the full audit log for
summaries = amounts.Where(a => zeroSummaries.Contains(a.Id))
.Concat(summaries.Where(s => s.Amount != 0));
Here's the output:

Related

How to join 2 data tables

DataTable1
LoginId LoginName SCount
1 Mohit 20
3 Riya 25
DataTable2
LoginId LoginName ECount
2 Smita 11
3 Riya 13
I want to show result like this
LoginName Scount Ecount Total
Mohit 20 0 20
Smita 0 11 11
Riya 25 13 38
Query:
DataTable dtResult = new DataTable();
DataTable UserCount1 = new DataTable();
DataTable UserCount2 = new DataTable();
// Assigning value to datatable
if (ds != null)
{
UserCount1 = ds.Tables["UserCount1"];
UserCount2 = ds.Tables["UserCount2"];
}
var LinqResult =
from dataRows1 in UserCount1.AsEnumerable()
join dataRows2 in UserCount2.AsEnumerable()
on dataRows1.Field<string>("LoginId") equals dataRows2.Field<string>("LoginId") into lj
from r in lj.DefaultIfEmpty()
select dtResult.LoadDataRow(new object[]
{
dataRows2.Field<string>("LoginName"),
r == null ? 0 : r.Field<int>("SCount"),
r == null ? 0 : r.Field<int>("ECount")
}, false);
Getting complie time error in
select statement( dataRows2.Field<string>("LoginName"),)
that dataRows2 does not exist in current context.
How to achieve that result?
For the easy and strongly typed solution, I would strongly suggest defining classes, such as:
class User1 { public int LoginId; public string LoginName; public int SCount; }
class User2 { public int LoginId; public string LoginName; public int ECount; }
to enable LINQ extension methods, then your task becomes quite easy (explanation in comments in code):
// Sample data.
DataTable UserCount1 = new DataTable();
DataTable UserCount2 = new DataTable();
UserCount1.Columns.AddRange(new DataColumn[] { new DataColumn("LoginId"), new DataColumn("LoginName"), new DataColumn("SCount") });
UserCount2.Columns.AddRange(new DataColumn[] { new DataColumn("LoginId"), new DataColumn("LoginName"), new DataColumn("ECount") });
UserCount1.Rows.Add(1, "Mohit", 20);
UserCount1.Rows.Add(3, "Riya", 25);
UserCount2.Rows.Add(2, "Smita", 31);
UserCount2.Rows.Add(3, "Riya", 13);
// Here we create lists of our users.
List<User1> users1 = new List<User1>();
List<User2> users2 = new List<User2>();
foreach (DataRow row in UserCount1.Rows)
users1.Add(new User1() { LoginId = int.Parse(row["LoginId"].ToString()), LoginName = (string)row["LoginName"], SCount = int.Parse(row["SCount"].ToString()) });
foreach (DataRow row in UserCount2.Rows)
users2.Add(new User2() { LoginId = int.Parse(row["LoginId"].ToString()), LoginName = (string)row["LoginName"], ECount = int.Parse(row["ECount"].ToString()) });
// Full outer join: first we join, then add entries, that were not included.
var result = users1.Join(users2, u1 => u1.LoginId, u2 => u2.LoginId, (u1, u2) => new { LoginId = u1.LoginId, LoginName = u1.LoginName, SCount = u1.SCount, ECount = u2.ECount, Total = u1.SCount + u2.ECount }).ToList();
result.AddRange(users1.Where(u1 => !result.Select(u => u.LoginId).Contains(u1.LoginId)).Select(u1 => new { LoginId = u1.LoginId, LoginName = u1.LoginName, SCount = u1.SCount, ECount = 0, Total = u1.SCount }));
result.AddRange(users2.Where(u2 => !result.Select(u => u.LoginId).Contains(u2.LoginId)).Select(u2 => new { LoginId = u2.LoginId, LoginName = u2.LoginName, SCount = 0, ECount = u2.ECount, Total = u2.ECount }));
Then you can construct another result DataTable, for which I don't see any reason.

LINQ not sorting List<> properly

My EF query is supposed to be sorting by the date of the first Product in the list, but for some reason, it only sorts most of the products and some of the dates are in the wrong order.
Here's the code...
using (var context = new SalesEntities())
{
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID, c.s84_Customer.CustomerName, c.SubdivisionID, c.s84_Subdivision.SubdivisionName, c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).ToList()
});
var finalList = groupedData.ToList().Where(x => x.Products.Last().Completed == false).ToList();
List<s84_Report_Project_POCO> lst = finalList.OrderBy(x => x.Products.First().ProductDate).ToList();
return lst;
}
Code seems good to me, but look at how one of the dates is out of order...
weird sorting http://www.84sales.com/weird_sort.png
Try doing the order by on the inital select
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID,
c.s84_Customer.CustomerName,
c.SubdivisionID,
c.s84_Subdivision.SubdivisionName,
c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped
.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).OrderBy(x => x.CustomerExpectedDate).ToList()
});
The problem is the .First() function, witch returns the first record, but not necessarly in date order. if you wich to order your grouped datas by date so that the First() function returns the most recent date, you'll need to order your datas before grouping them, and then REorder your results with the First()function :
using (var context = PrimaryConnection.returnNewConnection())
{
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID, c.s84_Customer.CustomerName, c.SubdivisionID, c.s84_Subdivision.SubdivisionName, c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped
.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).Orderby(t => t.CustomerExpectedDate).ToList()
});
var finalList = groupedData.ToList().Where(x => x.Products.Last().Completed == false).ToList();
List<s84_Report_Project_POCO> lst = finalList.OrderBy(x => x.Products.First().ProductDate).ToList();
All SQL queries (and hence Linq queries, when attached to a SQL database) have a random order, unless you sort them.
Products is not sorted - hence it has a random order.
You sort by Products.First(), but Products has a random order, so your sort will also be random.
Make sure Products is sorted within the query, and you should be ok.
Products = grouped.Select(....)
.OrderBy(x => x.ProductDate)
.ToList()

linq group by id and sum of amount getting null(undefined)

var approverlist = _db.ApprvRejClaimTravelCashByApproverIdIPhone(ProjectSession.CompanyUserId, ProjectSession.CompanyId, Status).AsEnumerable().ToList();`
var results =
from item in approverlist
group item by item.ClaimId
into g
select new
{
ClaimId = g.Key,
Name = g.First().Name,
Amount = g.Sum(s => s.Amount),
Description = g.First().Description,
Status = g.First().Status,
CreatedDate = g.First().CreatedDate,
SubmitedDate = g.First().SubmitedDate,
FullName = g.First().FullName,
ApproverStatus = g.First().ApproverStatus,
CurrencySymbol = g.First().CurrencySymbol,
chkbox = g.First().chkbox,
IsHierarchy = g.First().IsHierarchy,
ColumnCategory = g.First().ColumnCategory,
ColumnCategoryCode = g.First().ColumnCategoryCode
};
return Json(results.ToDataSourceResult(request));
getting undefined in column Amount when i bind this result to my kendo mvc grid,,,,any one have idea why this happens ?
Also tried below one but same result undefined :
var results = from x in approverlist
group x.ClaimId by new { x.ClaimId, x.Name, x.Description, x.Status, x.CreatedDate, x.SubmitedDate, x.FullName, x.ApproverStatus, x.CurrencySymbol, x.chkbox, x.IsHierarchy, x.ColumnCategory, x.ColumnCategoryCode }
into g
select new { g.Key.ClaimId, g.Key.Name, Amount = g.Sum(), g.Key.Description, g.Key.Status, g.Key.CreatedDate, g.Key.SubmitedDate, g.Key.FullName, g.Key.ApproverStatus, g.Key.CurrencySymbol, g.Key.chkbox, g.Key.IsHierarchy, g.Key.ColumnCategory, g.Key.ColumnCategoryCode };

Select from datatable with where clause

I am trying to select a few rows from a datatable. I am selecting two of the columns and how can I use a where clause in the below statement?
cars = new Head
{
heading = (string)dr["head"],
subHeads = dt.Select(r => new SubHead
{ // how to use a where clause here?
subHeading = (string)r["subhead"],
cars = dt.Select(r2 => new Cars
{ // how to use a where clause here?
name = (string)r2["name"],
quantity = (string)r2["qty"],
}).ToList()
}).ToList()
};
You could use,
dt.where(e => {check something}).Select({select code here})
Do this on both the places. Hope this helps.
You need to call the Where before you select. Example:
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
var contacts = ds.Tables["Contact"].AsEnumerable();
var orders = ds.Tables["SalesOrderHeader"].AsEnumerable();
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.Field<Int32>("ContactID") == order.Field<Int32>("ContactID"))
&& order.Field<decimal>("TotalDue") < 500.00M)
.Select(order => new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
Total = order.Field<decimal>("TotalDue")
}));
foreach (var smallOrder in query)
{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ",
smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
smallOrder.OrderID, smallOrder.Total);
}
taken from here.

How can I use this Linq Query using it with a multiple parameter by Join tables

var list = dc.Orders.
Join(dc.Order_Details,
o => o.OrderID, od => od.OrderID, <-- what if i have 2 more parameters let say an ID and a REC on both table. ex.: o=> o.OrderID && o.ItemName, od => od.OrderID && od.Itemname then (o, od) but its result is error? is there another way?
(o, od) => new
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
ShipName = o.ShipName,
Quantity = od.Quantity,
UnitPrice = od.UnitPrice,
ProductID = od.ProductID
}).Join(dc.Products,
a => a.ProductID, p => p.ProductID, <-- at this point too?
(a, p) => new
{
OrderID = a.OrderID,
OrderDate = a.OrderDate,
ShipName = a.ShipName,
Quantity = a.Quantity,
UnitPrice = a.UnitPrice,
ProductName = p.ProductName
});
is it possible to use this lambda expression linq query with multiple parameters by joining 3 tables?
--- UPDATE STILL ERROR -- :(
var header = DB.Delivery_HeaderRECs.Join(DB.Delivery_DetailsRECs, <-- Red Line on DB.Delivery_HeaderRECs.Join
q => new { q.drNO, q.RecNO },
qw => new { qw.DrNO, qw.RecNO },
(q, qw) => new
{
DR = q.drNO,
DATE = q.DocDate,
RECNO = q.RecNO,
CUSTID = q.CustomerID,
CUSTADDR = q.CustomerADDR,
RELEASE = q.ReleasedBy,
RECEIVE = q.ReceivedBy,
REMARKS = q.Remarks,
ITEM = qw.ItemCode,
DESC = qw.ItemDesc,
QTY = qw.Qty,
COST = qw.Unit,
PLATENO = qw.PlateNo,
TICKETNO = qw.TicketNo
}).Join(DB.Delivery_TruckScaleRECs,
w => new { w.DR, w.TICKETNO },
we => new { we.DrNo, we.TicketNO },
(w, we) => new
{
DR = w.DR,
DATE = w.DATE,
RECNO = w.RECNO,
CUSTID = w.CUSTID,
CUSTADDR = w.CUSTADDR,
RELEASE = w.RELEASE,
RECEIVE = w.RECEIVE,
REMARKS = w.REMARKS,
ITEM = w.ITEM,
DESC = w.DESC,
QTY = w.QTY,
COST = w.COST,
PLATENO = w.PLATENO,
TICKETNO = w.TICKETNO,
TRANSAC = we.TransactionType,
FWEIGHT = we.FirstWeight,
SWEIGHT = we.SecondWeight,
NWEIGHT = we.NetWeight
}).FirstOrDefault();
I made up changes base on the answer but an error said above the statement:
"The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly". i think it's talking about the parameters i've made..
You can use anonymous type for the Join like this:
var list = dc.Orders.Join(dc.Order_Details,
o => new { o.OrderID, o.ItemName},
od => new { od.OrderID, od.ItemName},
...);
The anonymous type will be compiled to use the autoimplemented Equals and GetHashCode so that the equality will be derived by the equality of all the corresponding properties. Just add more properties as you want in the the new {....}. Note that the order of properties provided in the 2 new {...} should be the same order of correspondence. The names should also be matched, you can explicitly specify the names to ensure this (this is needed in some cases) such as:
new {OrderID = o.OrderID, Name = o.ItemName}
However in your case the property names will be used as the same properties of the item.
UPDATE
This update is just a fix for your specific parameters, I said that the property names should be the same, if they are not you have to explicitly name them like this:
var list = dc.Orders.Join(dc.Order_Details,
q => new {DrNO = q.drNO, q.RecNO},
qw => new {DrNO = qw.DrNO, qw.RecNO},
...);

Categories

Resources