I am trying to group a list and using ToDictionary to achieve this which works:
var levels = ids.GroupBy(f => f.Id).
ToDictionary(g => g.Key, g => g.First().Name);
The problem is: in the string "Name" the last char is a number i.e. 2 or 5 or 7 etc.
I do NOT want to select the first but I want to select "Name" with the MAX number. How can i achieve this. example of Name can be: "abd-hbb-les3" , "abd-hbb-les1" , "abd-hbb-les6"
You could do this in the following way:
var levels = ids.GroupBy(f => f.Id).ToDictionary(g => g.Key,
g => g.First( x=> x.Name.Last() == g.Max( y=> y.Name.Last())).Name);
assuming that it's really about the last letter so it's not possible to have a two (or more) digits at the end e.g.:
abd-hbb-les16 //will not work with the above code
For every group simply select the name with the maximum last character of the string. Like this:
var levels = ids.
GroupBy(f => f.Id).
ToDictionary(
g => g.Key,
g => g.First(i => i.Name.Last() == g.Max(j => j.Name.Last())).Name);
Related
Lets say I've table with the structure as below:
MyRow:
Id Name Date
1 A 2015/01/01
2 B 2015/01/01
3 C 2015/01/02
4 A 2015/01/03
5 B 2015/01/03
6 A 2015/01/02
7 C 2015/01/01
Using EF I would like to get list of MyRow which would contain elements with distinct names and newest date so in this case it would be:
4 A 2015/01/03
5 B 2015/01/03
3 C 2015/01/02
I started with something like this:
var myRows = context.MyRows.GroupBy(mr => mr.Name).Select(..now wth with max..)
Order each group and take the last of each.
Or since EF doesn't (last time I checked) support Last(), order each group in reverse and take the first:
var myRows = context.MyRows
.GroupBy(mr => mr.Name)
.Select(grp => grp.OrderByDescending(mr => mr.Date).First());
var data = context.MyRows.Group(p => p.Name)
.Select(g => new {
Type = g.Key,
Date = g.OrderByDescending(p => p.Date)
.FirstOrDefault()
}
I have a list of files say prg_3.txt , prg_2.txt , prg_1.txt .
I need to loop over the files and merge the files in order 1,2,3 .
The query i am using is as follows:
var Groups = shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).ToList();
The above query would create a group names prg and it will have 3 files.
Now,i need to sort them in the order 1,2,3 i.e fromm their file names.
Here, I am getting grouped results, but i am not sure how to order the elements in each group
Please help..let me know incase of any questions..
Edited :
Will it be good enough ?
var userGroups = shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).Select(g=>g.OrderBy(x=>x.Substring(x.IndexOf('_',x.Length-x.IndexOf('_')))));
This should work but probably won't be so efficient:
shortfilenames
.GroupBy(s => s.Substring(0, s.IndexOf('_')))
.Select(
g => g.OrderBy(x => int.Parse(new String(x.Where(char.IsDigit).ToArray()))));
This will not work if your file contains additional digits, here is another solution to fix that, according to your comment this should work with the format you specified:
shortfilenames
.GroupBy(s => s.Substring(0, s.IndexOf('_')))
.Select(g => g.OrderBy(
x =>
{
var index = x.IndexOf('_');
return int.Parse(x.Substring(index + 1, x.LastIndexOf('.') - index));
}));
Since the names kinda match, what's the problem with simply using the OrderBy and giving it the names ?
var v = new string[] {"prg_3.txt","prg_2.txt", "prg_1.txt"};
var sorted = v.OrderBy(name => name);
you get :
prg_1.txt
prg_2.txt
prg_3.txt
If you want to sort inner groupings by file name this should do the trick:
shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_'))).Select(g => g.OrderBy(e => e)).ToList();
I am trying to filter the results on my query based on whether or not they contain the string "baby".
IEnumerable<ICD.ViewModels.HomeSearchViewModel> query =
ICDUnitOfWork.AlphaGroups.Find().GroupJoin(ICDUnitOfWork.Alphas.Find(),
a => a.AlphaGroupID,
g => g.AlphaGroupID,
(alphaGroups, alphas) =>
new ICD.ViewModels.
HomeSearchViewModel
{
AlphaGroups =
alphaGroups,
Alphas = alphas
})
.Where(row =>
row.AlphaGroups.Title.Contains("baby")
|| row.Alphas.Any(alpha => alpha.Title.Contains("baby"))
);
The problem is that when an Alpha.Title contains the string "baby" it should only show the Alpha's that contain "baby", rather than every alpha in the AlphaGroup. If the AlphaGroup.Title contains "baby" it should continue to show each alpha in the group. How can I accomplish this?
You could try something like the following:
IEnumerable<ICD.ViewModels.HomeSearchViewModel> query =
ICDUnitOfWork.AlphaGroups.Find()
.GroupJoin(
ICDUnitOfWork.Alphas.Find()
.GroupBy(a => new
{
BabyIndicator = a.Title.Contains("baby"),
GroupID = a.AlphaGroupID
}),
a => a.AlphaGroupID,
g => g.Key.GroupID,
(alphaGroups, alphas) =>
new ICD.ViewModels.HomeSearchViewModel()
{
AlphaGroups = alphaGroups,
Alphas = alphaGroups.Title.Contains("baby") ?
alphas.Select(g => g.AsEnumerable()).Aggregate((g1,g2) => g1.Concat(g2)) :
alphas.Aggregate(
(g1,g2) => g1.Key.BabyIndicator ?
g1 :
g2).AsEnumerable()
})
Logic:
The problem is that when an Alpha.Title contains the string "baby" it
should only show the Alpha's that contain "baby", rather than every
alpha in the AlphaGroup.
Here we group the alphas by groupID and whether they have babies, we then group join this onto the alphGroups. So we have four possibilities, no groups, one group without baby, one group with only babies and one of each. To pull this all together we aggregate. If there are no groups it returns no groups, if there is one group it returns that group, if there are two groups it returns only the one with babies.
If the AlphaGroup.Title contains "baby" it should continue to show
each alpha in the group.
Here we check whether an alphaGroup has a title baby, if it does return the whole grouping, if not apply alpha title logic
The LINQ query below is working fine but I need to tweak it a bit.
I want all the records in the file grouped by recordId (a customer number) and then ordered by, in descending order, the date. I'm getting the grouping and the dates are in descending order. Now, here comes the tweaking.
I want the groups to be sorted, in ascending order, by recordId. Currently, the groups are sorted by the date, or so it seems. I tried adding a .OrderBy after the .GroupBy and couldn't get that to work at all.
Last, I want to .take(x) records where x is dependent on some other factors. Basically, the .take(x) will return the most-recent x records. I tried placing a .take(x) in various places and I wasn't getting the correct results.
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = tokens[13],
date = Convert.ToDateTime(tokens[17])
}
)
.OrderByDescending (m => m.date)
.GroupBy (m => m.recordId)
.Dump();
Edit #1 -
recordId is not unique. There may / will likely be multiple records with the same recordId. recordId is actually a customer number.
The output will be a resultset with first name, last name, date, and recordId. Depending on several factors, there many be 1 to 5 records returned for each recordId.
Edit #2 -
The .Take(x) is for the recordId. Each recordId may have multiple rows. For now, let's assume I want the most recent date for each recordId. (select top(1) when sorted by date descending)
Edit #3 -
The following query generates the following results. Note each recordId only produces 1 row in the output (this is okay) and it appears it is the most recent date. I haven't thouroughly checked this yet.
Now, how do I sort, in ascending order, by recordId?
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = Convert.ToInt32(tokens[13]),
date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy (m => m.recordId)
.OrderByDescending (m => m.Max (x => x.date ) )
.Select (m => m.First () )
.Dump();
FirstName LastName recordId date
X X 2531334 3/11/2011 12:00:00 AM
X X 1443809 10/18/2001 12:00:00 AM
X X 2570897 3/10/2011 12:00:00 AM
X X 1960526 3/10/2011 12:00:00 AM
X X 2475293 3/10/2011 12:00:00 AM
X X 2601783 3/10/2011 12:00:00 AM
X X 2581844 3/6/2011 12:00:00 AM
X X 1773430 3/3/2011 12:00:00 AM
X X 1723271 2/4/2003 12:00:00 AM
X X 1341886 2/28/2011 12:00:00 AM
X X 1427818 11/15/1986 12:00:00 AM
You can't that easily order by a field which is not part of the group by fields. You get a list for each group. This means, you get a list of date for each recordId.
You could order by Max(date) or Min(date).
Or you could group by recordId and date, and order by date.
order by most recent date:
.GroupBy (m => m.recordId)
// take the most recent date in the group
.OrderByDescending (m => m.Max(x => x.date))
.SelectMany(x => x.First
The Take part is another question. You could just add Take(x) to the expression, then you get this number of groups.
Edit:
For a kind of select top(1):
.GroupBy (m => m.recordId)
// take the most recent date in the group
.OrderByDescending (m => m.Max(x => x.date))
// take the first of each group, which is the most recent
.Select(x => x.First())
// you got the most recent record of each recordId
// and you can take a certain number of it.
.Take(x);
snipped I had before in my answer, you won't need it according to your question as it is now:
// create a separate group for each unique date and recordId
.GroupBy (m => m.date, m => m.recordId)
.OrderByDescending (m => m.Key)
This seems very similar to your other question - Reading a delimted file using LINQ
I don't believe you want to use Group here at all - I believe instead that you want to use OrderBy and ThenBy - something like:
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = tokens[13],
date = Convert.ToDateTime(tokens[17])
}
)
.OrderBy (m => m.recordId)
.ThenByDescending (m => m.date)
.Dump();
For a simple Take... you can just add this .Take(N) just before the Dump()
However, I'm not sure this is what you are looking for? Can you clarify your question?
just add
.OrderBy( g=> g.Key);
after your grouping. This will order your groupings by RecordId ascending.
Last, I want to .take(x) records where
x is dependent on some other factors.
Basically, the .take(x) will return
the most-recent x records.
If you mean by "the most recent" by date, why would you want to group by RecordId in the first place - just order by date descending:
..
.OrderByDescending (m => m.date)
.Take(x)
.Dump();
If you just want to get the top x records in the order established by the grouping though you could do the following:
...
.GroupBy (m => m.recordId)
.SelectMany(s => s)
.Take(x)
.Dump();
If you want something like the first 3 for each group, then I think you need to use a nested query like:
var recipients = File.ReadAllLines(path)
.Select(record => record.Split('|'))
.Select(tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
RecordId = tokens[13],
Date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy(m => m.RecordId)
.Select(grouped => new
{
Id = grouped.Key,
First3 = grouped.OrderByDescending(x => x.Date).Take(3)
}
.Dump();
and if you want this flattened into a record list then you can use SelectMany:
var recipients = var recipients = File.ReadAllLines(path)
.Select(record => record.Split('|'))
.Select(tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
RecordId = tokens[13],
Date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy(m => m.RecordId)
.Select(grouped => grouped.OrderByDescending(x => x.Date).Take(3))
.SelectMany(item => item)
.Dump();
Here's the scenario:
Given a List of Outputs each associated with an integer based GroupNumber. For each distinct GroupNumber within the List of Outputs starting with the lowest GroupNumber (1). Cycle through that distinct group number set and execute a validation method.
Basically, starting from the lowest to highest group number, validate a set of outputs first before validating a higher groupnumber set.
Thanks,
Matt
There's almost too many ways to solve this:
Here's one for a void Validate method.
source
.GroupBy(x => x.GroupNumber)
.OrderBy(g => g.Key)
.ToList()
.ForEach(g => Validate(g));
Here's one for a bool Validate method.
var results = source
.GroupBy(x => x.GroupNumber)
.OrderBy(g => g.Key)
.Select(g => new
{
GroupNumber = g.Key,
Result = Validate(g),
Items = g.ToList()
})
.ToList();
If you need them as groups:
var qry = source.GroupBy(x=>x.GroupNumber).OrderBy(grp => grp.Key);
foreach(var grp in qry) {
Console.WriteLine(grp.Key);
foreach(var item in grp) {...}
}
If you just need them ordered as though they are grouped:
var qry = source.OrderBy(x=>x.GroupNumber);