linq group by, order by - c#

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());

Related

How to get the latest number using LINQ

Here lists a payment history of a customer in a db table
CustomerId PayId FeePaid
xx-yy-zz 37 0
xx-yy-zz 32 0
xx-yy-zz 31 30.00
xx-yy-zz 28 0
xx-yy-zz 26 0
xx-yy-zz 18 35.99
xx-yy-zz 17 0
xx-yy-zz 16 0
xx-yy-zz 9 12.00
xx-yy-zz 6 0
The PaymentId column is auto incremented.
How to get the last payment of this customer, i.e., the number $30.00?
My project is Asp.net API, so I need use LINQ to get the number.
If we assume that we're ignoring zeros, and that PayId is monotonically incrementing, then presumably:
as LINQ:
var val = ctx.SomeTable
.Where(x => x.CustomerId == customerId && x.FeePaid != 0)
.OrderByDescending(x => x.PayId)
.Select(x => x.FeePaid)
.First();
or as SQL:
select top 1 FeePaid
from SomeTable
where CustomerId = #customerId
and FeePaid <> 0
order by PayId desc
Try this linq expression:
var result = await (from d in _ctx.MyTable
where d.CustomerId="xx-yy-zz" && d.FreePaid > 0
orderby d.PayId descending
select d.FreePaid).FirstOrDefaultAsync();
tried to avoid negative queries
awaitable function
You wrote:
The PaymentId column is auto incremented
My advice would be to group the PaymentHistories per user, so by common value of CustomerId.
Then for each group, keep the PaymentHistory that has the highest value of PaymentId. After all: PaymentId is auto-increments, so the PaymentHistory in the group of PaymentHistories of Customer X is the one with the highest PaymentId
For this I used the overload of Queryable.GroupBy that has a parameter resultSelector, so I can precisely specify what I want in my result.
IQueryable<PaymentHistory> paymentHistories = ...
var lastCustomerPayments = paymentHistories.GroupBy(
// parameter keySelector: make groups with same CustomerId
paymentHistory => paymentHistory.CustomerId,
// parameter resultSelector: for every CustomerId and all PaymentHistories
// that have this value for CustomerId, make one new:
(customerId, paymentHistoriesWithThisCustomerId) => new
{
CustomerId = customerId,
// get the feePaid of the PaymentHistory with the largest PaymentId
FeePaid = paymentHistoriesWithThisCustomerId
.OrderByDescending(paymentHistory => paymentHistory.PaymentId)
.Select(paymentHistory => paymentHistory.FeePaid)
.FirstOrDefault(),
}
If you don't want FeePaid, but also the PaymentId, use the following resultSelector:
(customerId, paymentHistoriesWithThisCustomerId) => new
{
CustomerId = customerId,
LastPayment = paymentHistoriesWithThisCustomerId
.OrderByDescending(paymentHistory => paymentHistory.PaymentId)
.Select(paymentHistory => new
{
PaymentId = paymentHistory.PaymentId,
FeePaid = paymentHistory.FeePaid,
})
.FirstOrDefault();
}

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)
};

using distinct in DataTable.Select function

I have a data table and I want to populate two extra datatables using this datatable,here is a simple form of my table
My data table columns are
[name][family][id][propertyid][propertyEnergy]
John smith 1 12 Gas
John smith 1 13 Gas
John smith 1 14 null
John smith 1 15 Gas
Hannah smith 2 16 Gas
Hannah smith 2 17 Gas
Hannah smith 2 18 Gas
I want to use this query in datatable select distinct [name][family][id] from table
which results
John smith 1
Hannah smith 2
and again I use this query in another datatable select [id][propertyid][propertyEnergy] from table which results
1 12 Gas
1 13 Gas
1 14 null
1 15 Gas
2 16 Gas
2 17 Gas
2 18 Gas
I searched and found that I can DataTable.Select but examples that I have seen shows that I can only add Where sentense to DataTable.Select and I have no idea how to perform things like Distinct in it,
Can you please help me or give me some hints how to do it?
Thank you so much
I'd use Linq-To-DataTable instead:
var distinctNames = table.AsEnumerable()
.Select(row => new
{
Name = row.Field<string>("Name"),
Family = row.Field<string>("Family"),
ID = row.Field<int>("ID")
})
.Distinct();
var distinctProperties = table.AsEnumerable()
.Select(row => new
{
ID = row.Field<int>("ID"),
PropertyID = row.Field<int>("PropertyID"),
PropertyEnergy = row.Field<int>("PropertyEnergy")
})
.Distinct();
If you need two additional DataTables you have to create and fill them manually since the columns are different than the main-table. You can fill them in a loop from the queries above.
This should work as it is:
string[] nameColumns = { "Name", "Family", "ID" };
DataTable tblNames = table.Clone();
var removeColumns = tblNames.Columns.Cast<DataColumn>()
.Where(c => !nameColumns.Contains(c.ColumnName)).ToList();
removeColumns.ForEach(c => tblNames.Columns.Remove(c));
foreach (var x in distinctNames)
tblNames.Rows.Add(x.Name, x.Family, x.ID);
string[] propertyColumns = { "ID", "PropertyID", "PropertyEnergy" };
DataTable tblProperties = table.Clone();
removeColumns = tblProperties.Columns.Cast<DataColumn>()
.Where(c => !propertyColumns.Contains(c.ColumnName)).ToList();
removeColumns.ForEach(c => tblProperties.Columns.Remove(c));
foreach (var x in distinctProperties)
tblProperties.Rows.Add(x.ID, x.PropertyID, x.PropertyEnergy);

linq query to fetch employee name of those employee who is working on exactly 3 projects

i an having two tables as follows:
1)employee
2)project
employee table records:
empid empname project_id
1 abc 101
2 pqr 100
3 lmn 99
4 abc 99
5 abc 100
project table:
pid pname
99 jknkj
100 nkj
101 kjkjn
now i want to fetch employee name of those employee who is working on exactly 3 project?
i want linq query.can anybody with me with linq query???
if you want to fetch employees who are working on exactly 3 projects, do something like this:
var Counts =
from e in employees
group p by e.empname into g
where g.Count == 3
select new { Employee = g.Key, ProjectCount = g.Count() };
and if you want to want fetch project on which exactly 3 employees working then do like this:
var Counts =
from e in employees
group p by e.projectid into g
where g.Count == 3
select new { Porject= g.Key, EmployeeCount = g.Count() };
You can use GroupBy and group your Employees based on project_id then get the groups that contains exactly 3 employees:
db.Employees.GroupBy(x => x.project_id)
.Where(x => x.Count() == 3)
.SelectMany(x => x.Select(e => e.empname));
Edit: the below comment is correct, you can group your records by emp_name in order the fix that issue:
db.Employees.GroupBy(x => x.empname)
.Where(x => x.Count() == 3)
.Select(x => x.Key);

Linq query to sum by group

I have a data table like this:
Category Description CurrentHours CTDHours
LC1 Cat One 5 0
LC2 Cat Two 6 0
LC3 Cat Three 18 0
LC1 Cat One 0 9
LC2 Cat Two 0 15
LC4 Cat Four 0 21
That I need to Group and Sum to this:
Category Description CurrentHours CTDHours
LC1 Cat One 5 14
LC2 Cat Two 6 21
LC3 Cat Three 18 0
LC4 Cat Four 0 21
In other words I need to sum the two Hours columns grouping by the Category and Description columns.
I know that I could build a new table and loop through the existing data and sum the data into the new table but I thought there would be an easier way to do it using Linq. I've googled it for a few hours but all the examples I found didn't seem to fit what I was trying to do.
BTW, the odbc driver that creates the data table does not have the capability for sub queries, etc. or I would have just done it using SQL.
Use anonymous object to group by category and description. Here is Linq to DataSet query which returns grouped hours:
from r in table.AsEnumerable()
group r by new {
Category = r.Field<string>("Category"),
Description = r.Field<string>("Description")
} into g
select new {
Category = g.Key.Category,
Description = g.Key.Description,
CurrentHours = g.Sum(x => x.Field<int>("CurrentHours"),
CTDHours = g.Sum(x => x.Field<int>("CurrentHours") + x.Field<int>("CTDHours"))
}
If you are querying database (not clear from question):
from r in context.Table
group r by new {
r.Category,
r.Description
} into g
select new {
g.Key.Category,
g.Key.Description,
CurrentHours = g.Sum(x => x.CurrentHours),
CTDHours = g.Sum(x => x.CTDHours + x.CurrentHours)
}
You need to sum CurrentHours and CTDhours, so -
select new {
...
CTDHours = g.Sum(x => x.Field<int>("CTDHours") + g.Sum(x => x.Field<int>("CurrentHours")
}

Categories