Linq Grouping items by property - c#

I'm trying to group an object by one property (State). The end goal is to have a list. And within each list is an object consisting of two properties. I have data that looks like the following...
State Name ID
CA John 12
CA Kevin 13
CA Joe 14
AZ Sally 15754
AZ Stuart 1263
TN Sam 1211
How would one go about using linq to make this a list of object 'People' consisting of a person's name and ID grouped by state? This is what I've tried...but it's not working
var result = from groupOne in dataset
group groupOne by OneGroup.State into g
select new People() { People = g.ToList() }.ToList();

Why are you grouping by OneGroup.State, when OneGroup is not a part of the query?
That one should do the trick:
var result = from groupOne in dataset
group groupOne by groupOne.State into g
select new {
State = g.Key,
People = g.Select(x => new { x.Name, x.ID })
};
It returns anonymous types, but you can easily change it to return your class objects if you need.

Related

sorting by using linq comparing two lists?

I have 2 list of items;
IEnumerable<Investigator> investigators = RepositoryContext.InvestigatorRep.GetInvestigators(site.Site.Id, out totalCount);
var NewInvestigators = base.ActivePage.Investigators;
I have 30 items in investigators and 20 items in NewInvesigators, both have property Id, InvId I need to match that.
var all = investigators.Where(b => crInvestigators.Any(a => a.InvestigatorId == b.Id));
I tried this but not worked
I want to create a new list based on matching Id of those two lists. If Id matches get the particular investigator(basically a sort on investigators based on Id existing in NewInvesigators).
I can do it by using for each, but I want to know whether it is possible with linq?
in newinvestigator I have object which is having two property, investigatorId and Name.
In Investigator I have property Id , City , country.
no Name or investigatorId in the investigator list.
You could try something like this:
var result = investigators.Where(inv=>NewInvestigators.Any(ninv=>ninv.id == inv.investigatorId))
.OrderBy(inv=>inv.id);
Another way to get the same result is using a join.
var result = from inv in investigators
join ninv in NewInvestigators
on inv.id equals ninv.investigatorId
order by inv.id
select inv;

How do I use Group By and MAX in Linq to return multiple rows?

Clarified example
I have a database of users that is created by a script that scans through Active Directory. One of the fields it applies is a "ScanDate" field, which indicates when the scan took place. The script scans through multiple Active Directory domains.
GOAL: Obtain an IList from the database that contains the list of users for ALL domains, but where the ScanDate is the MAX(ScanDate) for each domain.
This ensures I get the freshest data for each domain.
A SQL query that appears to work for me:
SELECT *
FROM ADScans a
WHERE a.ScanDate = (SELECT MAX(b.ScanDate) FROM ADScans b WHERE a.Domain = b.Domain) AND Enabled = 1
However, having trouble getting that expressed in LINQ
e.g.:
Category | Date
Cat1 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Cat1 | 4/3/16
I would expect a list:
Cat1 | 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Some clarification
I would expect to have multiple rows returned per category - the MAX(Date) will not just give me one. I am looking to obtain ALL of the rows for the MAX(Date) of each category.
Something like this should work:
var result =
list
//Group by Category
.GroupBy(x => x.Category)
//For each Category, select Category and max Date within the Category
//This would create an anonymous object, you could do a "new Entity" instead if you want
.Select(g => new {Category = g.Key, Date = g.Max(x => x.Date)})
.ToList();
I'm an idiot.
from u in this.db.ADScans
where u.ScanDate ==
(from s in this.db.ADScans where u.Domain == s.Domain select s.ScanDate).Max()
&& u.Enabled
select u;
Rather than using Max(), just order the items in the groups and take the top item: since you ordered the items, it's guaranteed to be the highest one:
var mostRecentScanFromEachDomain =
from u in this.db.ADScans
where u.Enabled
group u by u.Domain into g
select g.OrderByDescending(u => u.ScanDate)
.FirstOrDefault();
You can GroupBy by Domain to get the max ScanDate, then keep only the rows with that Date.
For a model like this:
class ADScan
{
public int Domain { get; set; }
public DateTime ScanDate { get; set; }
}
You can get the scans doing this:
var result = scans.GroupBy(s => s.Domain)
.SelectMany(g => g.Where(s => s.ScanDate == g.Max(d => d.ScanDate)));
This produces a collection containing the scans with the max ScanDate for its Domain.

Concatenate ids based off of same Name in Entity Framework/Linq

Given a list of states that have location ids:
location_id name
----------- ----
1546 Arizona
8543 Arizona
7894 Arizona
8456 Maine
8354 New York
1268 New York
I am selecting from this table as such
var query = (from s in db.Locations
//A bunch of joins and where clause to filter list
select new { s.location_id, s.name });
I would like to get a list that contains
location_id name
----------- ----
1546,8543,7894 Arizona
8456 Maine
8354,1268 New York
How would I go about this?
I read that entity framework can't translate String.Join so I would have to call ToList() first and then select from that list joining the location ids but when I do it I get the same list that I started with.
How can I get the result I am looking for?
Thank you.
Just group:
var query2 = from l in query.AsEnumerable()
group l by l.name into g
select new {
location_id = String.Join(",", g.Select(x=>x.location_id.ToString())),
name = g.Key
};
I believe you'll need the AsEnumerable() call because you cannot translate a String.Join into SQL. You can of course ToList() instead if you prefer to eagerly load. However, as #Servy points out, you should do the grouping on the database side:
var query2 = from g in query.GroupBy(l => l.name).AsEnumerable()
select new {
location_id = String.Join(",", g.Select(x=>x.location_id.ToString())),
name = g.Key
};
Essentially all you're doing here is a GroupBy. You can then manipulate the results of the group in linq to objects, rather than the query, after you've pulled the results:
var dbquery = (from s in db.Locations
//A bunch of joins and where clause to filter list
group s.location_id by s.name into locations
select new { locations, name = locations.Key });
var query = dbquery.AsEnumerable()
.Select(group => new
{
name = group.name,
locations = string.Join(",", group.locations)
});

Need to group LINQ to Entities Results

Here is my code:
var context = new InventoryContext();
var res = from i in context.Inventories
select new FullInventory
{
InventoryID = i.InventoryID,
ItemModelID = i.ItemModelID,
ModelName = i.ItemModel.ModelName,
...
Quantity = context.Inventories.Select(x => x.ItemModelID).Count()
};
return res.ToList();
The code I have works great, however the results look like this:
CATEGORY | MANUFACTURER | MODEL | QUANTITY
1. Hard drive | Dell | 250GB | 2
2. Hard drive | Dell | 250GB | 2
As you can see, I have 2 entries in the table and since those entries are exactly the same, I would like them to be grouped together. To group them together I need to group by ItemModelID, however I still need to put all of the data into a list of FullInventory classes.
How can I group my data together while still "selecting" the data into a list of FullInventory classes?
Group by that value and then just select out the first item from each group using let.
var res = from inventory in context.Inventories
group inventory by inventory.ItemModelID into model
let i = model.FirstOrDefault()
select [...]
I would attempt the Group By after retrieving the full list with a Lambda expression:
var context = new InventoryContext();
return (from i in context.Inventories
select new FullInventory
{
InventoryID = i.InventoryID,
ItemModelID = i.ItemModelID,
ModelName = i.ItemModel.ModelName,
...
Quantity = context.Inventories.Select(x => x.ItemModelID).Count()
}).GroupBy(g => g.ItemModelID).ToList();
You may use Distinct()
return res.Distinct().ToList()

Duplicate values using linq

I have the Input format in following way
S.no Name wages
1 Tom $200
1 Tom $300
1 Tom $400
2 Rob $500
2 Rob $600
Result set should be in the following way
Name OriginalWage DuplicateWage
Tom $200 $300
Tom $200 $400
Rob $500 $600
I should leave the first record and take the duplicate records into count .Here the original wages is the wage of the first distinct record.
How can i accomplish the result set using linq.
This is the one i tried so far
//Get duplicate values
Dim Duplicates = wageRecordList.GroupBy(Function(w) w.serialnumber).Where(Function(d) d.Count > 1)
//load duplicates to a list
lstDuplicateRecords=Duplicates
//Read list--This one is a hard coded sort of thing and works only for one set of duplicate values
lstResult = (From duplicateRecords In lstDuplicateRecords.Skip(1) Select serialnumber= duplicateRecords.serialnumber, Name= duplicateRecords.Name, OriginalWages= CType(lstDuplicateRecords(0).Wages, String), _
DuplicateWages = CType(duplicateRecords.wages, String))
You can make something like this
var groupedUsers = from user in users
group user by user.User into userGroup
select new
{
User = userGroup.Key,
userHobies =
userGroup.Aggregate((a, b) =>
new { User = a.User, Hobby = (a.Hobby + ", " + b.Hobby) }).Hobby
}
;
foreach (var x in groupedUsers)
{
Debug.WriteLine(String.Format("{0} {1}", x.User, x.userHobies));
}
code is not mine and has been taken from: Use LINQ to concatenate multiple rows into single row (CSV property)
this link might be helpful too
EDITED
Sorry, misunderstood you question
something like this can do the trick
var query = from sal in _yourcontext
join salmin in ( from sal1 in _yourcontext
group sal1 by sal1.name into group
select new{
Name = group.Key
MinSal = group.Min(sal1=>sal1.Salary))
}
on sal.Name equals salmin.Name into result
where sal.Salary != salmin.MinSal
select new{ salmin.Name,salmin.MinSal,sal.Salary }
I managed to get the result set you wanted (proved a nice challenge):
It is assuming that the first record it finds for an employee is the original wage however...
var result = from employeeWages in GetEmployees().GroupBy(e => e.Name)
from duplicateWage in employeeWages.Skip(1).Select(e => e.Wage)
select new
{
Name = employeeWages.Key,
OriginalWage = employeeWages.First().Wage,
DuplicateWage = duplicateWage
};
A full LinqPad script is here for testing: http://share.linqpad.net/wgxcns.linq
Example Result

Categories

Resources