Entity framework where, order and group - c#

I'm using the following LINQ to select data from a table:
(from m in entity.Results
where m.Group == 0 ||
m.Group == 1
orderby m.Points descending
select m);
This gives me a result of all Users who are in Group 1 or 2. With that i can display the points they have. But this shows me the points they have in Group 1 and Group 2 separately.
How can i group them and display the total points they have? So instead of this (What i have now):
user1 - group1 - 10
user1 - group2 - 7
user2 - group1 - 7
user2 - group2 - 5
I want this:
user1 - total: 17
user2 - total: 12
How do i have to adjust my query to get a result set like that?

You need to group the users, then use Sum to calculate the TotalPoints:
from m in entity.Results
where m.Group == 0 || m.Group == 1
group m by m.User into g
let TotalPoints = g.Sum(m => m.Points)
orderby TotalPoints descending
select new { User = g.Key, Username = g.Key.Username, TotalPoints };

entity.Results
.Where(m => m.Group == 0 || m.Group == 1)
.GroupBy(m => m.UserID)
.Select(m => new { User = m.Key, TotalPoints = m.Sum(v => v.Points) })
.OrderByDescending(m => m.TotalPoints);

Hi Vivendi use this(Please edit according to your requirement)
var q = (from h in entity.Results
group h by new { h.UserID} into hh
select new {
hh.Key.UserID,
Score = hh.Sum(s => s.Points )
}).OrderByDescending(i => i.Points);
Output
total: 17
total: 12

Another example with more than one sum and a join
from e in _context.LearnResults
join c in _context.Country on e.CountryId equals c.CountryId
where c.DomainId.Equals("xx")
group e by e.Country.Name into newCountry
let Approved = newCountry.Sum(e => e.Approved)
let Total = newCountry.Sum(e => e.Total)
select new LearnResults() { CountryName = newCountry.Key, Approved= Approved, Total=Total };

Related

Group by results in ling

I have linq statement that returns value. The LINQ statement below will returns the systemUser Id and totalHours of a systemUser as shown below. However, what I really what is to get the Id and the total hours per system user. How to add group then in linq?
var th1 = (from ss in db.SystemUsers
join t in db.Timesheets on ss.Id equals t.SystemUser
where (t.Project == projectId && t.StartTime >= year_start && t.StartTime < year_end)
select new
{
ss.Id,
t.TotalHours
});
Id | TotalHours
1 | 10
1 | 20
2 | 10
2 | 5
and so on...
EXPECTED OUTPUT
Id | TotalHours
1 | 30
2 | 15
and so on...
You have to use GroupBy
var th1 =
from ss in db.SystemUsers
join t in db.Timesheets on ss.Id equals t.SystemUser
where (t.Project == projectId && t.StartTime >= year_start && t.StartTime < year_end)
group t by new { ss.Id } into g
select new
{
g.Key.Id,
TotalHours = g.Sum(x => x.TotalHours)
};

How to display monthly attendance data

I have two tables
TblEmployee
attendance_id employee_id Scan_type Date_and_time
1 1 IN 15-Jan-19 8:00:00 AM
2 1 IN 15-Jan-19 8:00:02 AM
3 2 IN 15-Jan-19 8:05:01 AM
4 2 OUT 15-Jan-19 4:00:00 PM
5 1 IN 16-Jan-19 8:05:30 AM
AttendanceTable
emp_id emp_name
1 Salman
2 Tahir
3 Jameel
I want to display monthly record data of all employees with in and out times. In time will be the first time with "iN" and out time will be the last time with "OUT". Employee may forget to sign-in or sign-out so in that case the field will remain blank or show "absent".
I am trying to do like this but don't know what to do next
var InList = from a in _context.TblEmployee
from e in _context.AttendanceTable.Where(x =>
a.EmpId == x.EmployeeId)
.Where(x => x.ScanType == "IN")
.OrderBy (x => x.DateAndTime)
.DefaultIfEmpty()
select new
{
AttDate = e.DateAndTime.Date,
Emp_name = a.EmployeeName,
Emp_Id = e.EmployeeId
};
var OutList = from a in _context.TblEmployee
from e in _context.AttendanceTable.Where(x =>
a.EmpId == x.EmployeeId)
.Where(x => x.ScanType == "OUT")
.OrderBy (x => x.DateAndTime)
.DefaultIfEmpty()
select new
{
AttDate = e.DateAndTime.Date,
Emp_name = a.EmployeeName,
Emp_Id = e.EmployeeId
};

Linq select all items where in list with groupby

I have the following data as a list:
raceId data position
1 A 0
1 B 0
1 F 1
1 J 0
2 A 2
2 F 1
3 A 0
3 J 2
3 M 1
3 V 3
I need to get the total (count) of races where there are ALL matching letters with the same raceid.
I.E a search on 'A' and 'J' = 2 (race's 1 and 3)
In addition I need to get the position data for each.
raceId data position
1 A 0
1 J 0
3 A 0
3 J 2
So far I have the following code.
var dataValues = new string[] { 'A', 'J' };
var races = raceData
.GroupBy( ac => ac.raceId )
.Select( grp => grp.First() )
.Where( t =>
dataValues
.All( s =>
dataValues
.Contains( t.data )
)
);
var racecount = races.count()
The issue is that this returns all raceId values where there is either letter in the data.
This should work for you:
var results = raceData.GroupBy(rd => rd.raceId)
.Where(g => dataValues.All(dv => g.Select(g2 => g2.data).Contains(dv)));
int raceCount = results.Count();
var results2 = results
.SelectMany(g => g)
.Where(rd => dataValues.Contains(rd.data));
raceCount will give you 2 and results2 will give you the 4 records you're expecting.
It works for me with your provided data anyway!
var groupedRaces = from r in raceData
group r by r.raceId into gp
select new { raceId = gp.Key, Datas = gp.Select(g => g.data).ToArray() };
var raceIds = from r in groupedRaces
where dataVals.All(mv => r.Datas.Contains(mv))
select r.raceId;
var races = from r in raceData
where raceIds.Contains(r.raceId) && dataVals.Contains(r.data)
select r;
Try this,
list.GroupBy(t => t.raceID).SelectMany(k => k).Where(x => dataValues.Contains(x.data))
.Select(f=> new { f.data ,f.position,f.raceID}).ToList();
Result,
Hope helps,

How can I get a Distinct for each part of a pair Identifier

I have a table that allocates a Truck to a User, using their Identities to link each allocation to their respective entities. Only the last allocation for a Truck/User can be selected. I used the following LINQ to get the values I need
var query = (from ta in TruckAllocations
where ta.Truck.Name != null || !ta.Truck.Name.Contains(string.Empty)
group ta by ta.UserId into grp
let MaxDate = grp.Max(g => g.CreatedOn)
from g in grp
where g.CreatedOn == MaxDate
select new { Key = g.TruckAllocationId, Truck = g.TruckId, User = g.UserId, Date = g.CreatedOn });
The problem I'm having is that two User's that are allocated to the same truck are in the result of the query, and I just want the last on assigned. Example of result
47 26 224 2014-03-28 10:47 AM
48 3 149 2014-03-31 10:25 AM
1048 2 575 2014-04-04 1:14 PM
1049 23 575 2014-04-09 9:15 AM
1050 56 322 2014-04-09 2:27 PM
As you can see User [2 and 23] are assigned the same Truck [575]
the result that I want would be this:
47 26 224 2014-03-28 10:47 AM
48 3 149 2014-03-31 10:25 AM
1049 23 575 2014-04-09 9:15 AM
1050 56 322 2014-04-09 2:27 PM
Where record 1048, doesn't show because he was not the last User assigned to the Truck.
Is there a way to get a Distinct on the pair where each value in the pair has to be unique
UPDATE
I also tried
var query = (from ta in TruckAllocations
where ta.Truck.Name != null || !ta.Truck.Name.Contains(string.Empty)
group ta by ta.TruckId into grp
let MaxDate = grp.Max(g => g.CreatedOn)
from g in grp
where g.CreatedOn == MaxDate
select new { Key = g.TruckAllocationId, Truck = g.TruckId, User = g.UserId, Date = g.CreatedOn });
Result is:
6 54 3 2014-03-04 7:03 PM
8 63 4 2014-03-13 12:36 PM
10 626 4 2014-03-13 2:10 PM
13 12 4 2014-03-20 4:18 PM
21 8 3 2014-03-20 6:21 PM
42 10 3 2014-03-21 1:09 PM
43 3 26 2014-03-26 5:35 PM
44 32 1 2014-03-27 8:53 AM
47 224 26 2014-03-28 10:47 AM
48 149 3 2014-03-31 10:25 AM
1049 575 23 2014-04-09 9:15 AM
1050 322 56 2014-04-09 2:27 PM
It does give me only one Truck/User for the Truck as the record [1048] is not returned, but now I have Use 4,3, 26 assigned to multiple trucks, when they can only be assigned one
And grouping with TruckId and UserId will add the Truck assigned to two Users, to the one grouped by TruckId.
var query = (from ta in TruckAllocations
where ta.Truck.Name != null || !ta.Truck.Name.Contains(string.Empty)
group ta by new {ta.TruckId, ta.UserId} into grp
let MaxDate = grp.Max(g => g.CreatedOn)
from g in grp
where g.CreatedOn == MaxDate
select new { Key = g.TruckAllocationId, Truck = g.TruckId, User = g.UserId, Date = g.CreatedOn });
** UPDATE2 **
The solution that got me what I wanted was to query the table grouped by UserId (1st query in question), then query the table grouped by the TruckId (2nd query in question). Then query both results to get matches on truck and user like this:
var assigned = (from t1 in query1
from t2 in query2
where t1.Truck == t2.Truck && t1.User == t2.User
select new {t1.Key, t1.Truck, t1.User, t1.Date}).ToList();
Would like to know if there was a better way to do it though.
You could do something like that (group, order by descending on the wanted element).
More used to fluent syntax, so...
var result = TruckAllocations
.Where(ta => ta.Truck.Name != null && ta.Truck.Name != string.Empty)
.GroupBy(ta => ta.UserId)
//take the first element of each group, which will be the one with the max CreatedOn date
.Select(g=> g.OrderByDescending(m => m.CreatedOn).First())
.Select(m => new {
Key = m.TruckAllocationId,
Truck = m.TruckId,
User = m.UserId,
Date = m.CreatedOn
});
EDIT
You may do this in one or two steps.
CAUTION : If all trucks used by a given user have been used later by another User => this User won't appear in list.
2 steps version
var lastDateByTruck = TruckAllocations
.Where(ta => ta.Truck.Name != null && ta.Truck.Name != string.Empty)
.GroupBy(m => m.TruckId)
.Select(m => new
{
TruckId = m.Key,
CreatedOn = m.Max(x => x.CreatedOn)
});
var result = from allocation in TruckAllocations
join lastUsage in lastDateByTruck on new
{
allocation.TruckId,
allocation.CreatedOn
} equals new
{
lastUsage.TruckId,
lastUsage.CreatedOn
}
group allocation by allocation.UserId
into g
select new
{
UserId = g.Key,
TruckId = g.First().TruckId,
CreatedOn = g.First().CreatedOn
};
All in one with fluent syntax :
var lastDateByTruck = TruckAllocations
.Where(ta => ta.Truck.Name != null && ta.Truck.Name != string.Empty)
.GroupBy(m => m.TruckId)
.Select(m => new
{
TruckId = m.Key,
CreatedOn = m.Max(x => x.CreatedOn)
})
.Join(TruckAllocations, x => new{x.TruckId, x.CreatedOn}, y => new{y.TruckId, y.CreatedOn}, (x, y) => y)
.GroupBy(m => m.UserId)
.Select(g => new
{
UserId = g.Key,
TruckId = g.First().TruckId,
CreatedOn = g.First().CreatedOn
});

Group rows and get max from each group.

Below I have table - Company
id name value year
1 IBM 10 2011
2 IBM 30 2012
3 IBM 10 2012
4 C 10 2010
I want to group records by name and from each group return only one record with maximum id. All results combine into the list of companies using linq where year is greater 2011. For my example output should be - "3 IBM 10 2012"
I did write something but does not working.
var a = from x in companies where x.year > 2011
group x by new {x.name, x.value, x.ID, x.year } into g
select new {
g.Key.name,
g.Key.value,
g.Max(a=>a.ID),
g.Key.value
};
return a.ToList();
Try this:
var a = from x in companies
where x.Year > 2011
group x by new { x.Name } into g
from x1 in companies
where x1.ID == (from x2 in g select x2.ID).Max()
select x1;
Or something more efficient:
var a = from x in companies
where x.Year > 2011
group x by new { x.Name } into g
join x2 in companies on (from x3 in g select x3.ID).Max() equals x2.ID
select x2;
Don't include the ID in your grouping. In fact, if you just want them grouped by company name, don't include any of those other properties either:
// set up for testing
var companies =
from c in new[]{"1,IBM,10,2011", "2,IBM,30,2012", "3,IBM,10,2012", "4,C,10,2010"}
let cp = c.Split(',')
select new {id=int.Parse(cp[0]), name=cp[1], value=int.Parse(cp[2]), year=int.Parse(cp[3])};
// query
var q = from x in companies where x.year > 2011
group x by x.name into g
let top = g.OrderByDescending(x => x.id).FirstOrDefault()
select new {
top.name,
top.value,
top.id,
top.year
};

Categories