Linq IEnumerable<IGrouping<string, Class>> back to List<Class> - c#

How can I turn the following statement back to List<DocumentData>
IEnumerable<IGrouping<string, DocumentData>> documents =
documentCollection.Select(d => d).GroupBy(g => g.FileName);
the goal is to get List that should be smaller than documentCollection.
FileName contains duplicates so I want to make sure I don't have duplicate names.
I have also tried the following but it's still providing me with duplicate file names
documentCollection =
documentCollection.GroupBy(g => g.FileName).SelectMany(d => d).ToList();

Each IGrouping<string, DocumentData> is an IEnumerable<DocumentData>, so you could simply call SelectMany to flatten the sequences:
var list = documents.SelectMany(d => d).ToList();
Edit: Per the updated question, it seems like the OP wants to select just the first document for any given filename. This can be achieved by calling First() on each IGrouping<string, DocumentData> instance:
IEnumerable<DocumentData> documents =
documentCollection.GroupBy(g => g.FileName, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();

You haven't said what T should stand for in List<T> you're looking for, so here are couple the most likely to be desired:
List<DocumentData> - rather pointless as you already have that on documentCollection
var results = documents.SelectMany(g => g).ToList();
List<KeyValuePair<string, List<DocumentData>>
var results =
documents.Select(g => new KeyValuePair(g.Key, g.ToList())).ToList();
List<string> - just the names
var results = documents.Select(g => g.Key).ToList();
List<IGrouping<string, DocumentData>>
var results = documents.ToList();

Related

How do I select all distinct strings in a list of lists of another type?

I'm still very new with LINQ. I have the following "simplified" data structure:
List<List<Field>> myData = new List<List<Field>>();
Field consists of two string members, Type and Name.
My goal is to get a List<string> containing all distinct Name corresponding to a given Type. My first approach is this:
var test = myData
.Where(a => a.FindAll(b => b.Type.Equals("testType"))
.Select(c => c.Name)
.Distinct());
Does somebody have a hint for me? =)
You just need to use SelectMany to flatten your list of lists and then proceed as normal
var test = myData.SelectMany(x => x)
.Where(x => x.Type == "testType")
.Select(x => x.Name)
.Distinct()
.ToList();
Or in query syntax
var test = (from subList in myData
from item in subList
where item.Type == "testType"
select item.Name).Distinct().ToList();
Another way to do it using query notation:
var test= from list in myData
from e in list
where e.Type=="testType"
group e.Name by e.Name into g
select g.Key;
But is better go for one of the #juharr's solutions

Group By struct list on multiple columns in C#

I am having a struct as
public struct structMailJob
{
public string ID;
public string MailID;
public int ResendCount;
public int PageCount;
}
and a list as
List<structMailJob> myStructList = new List<structMailJob>();
I have loaded data in myStructList from database and want myStructList data in a new list after grouping by MailID and ResendCount.
I am trying as:
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList.GroupBy(u => u.MailID, u=>u.ResendCount)
.Select(grp => new { myStructList = grp.ToList() })
.ToList();
but unable to do that as getting error message - cant implicitly convert generic list to structMailJob.
I think that you are looking for is the following:
var newStructList = myStructList.GroupBy(smj => new { smj.MailID, smj.ResendCount })
.Select(grp => new
{
MailID = grp.Key.MailID,
ResendCount = grp.Key.ResendCount
MailJobs = grp.Select(x=>new
{
x.ID,
x.PageCount
}).ToList()
})
.ToList();
Note that we changed the GroupBy clause to the following one:
GroupBy(smj => new { smj.MailID, smj.ResendCount })
Doing so, the key on which the groups would be created would be consisted of both MailID and ResendCount. By the way the former GroupBy clause isn't correct.
Then having done the grouping, we project each group to an object with three properties, MailID and ResendCout, which are the components of the key and list of anonymous type object with two properties, ID and PageCount, which we gave it the name MailJobs.
Last but not least you will notice that I didn't mention the following
List<structMailJob> newStructList = new List<structMailJob>();
I just used the var and declared the newStructList. I don't think that you stated in your post makes sense. How do we expect to get a list of the same objects after grouping them? So I assumed that you might want is the above.
However, I thought you might want also something like this and you didn't want to refer to Grouping.
myStructList = myStructList.OrderBy(smj => smj.MailID)
.ThenBy(smj => smj.ResendCount)
.ToList();
Linq Query is completely incorrect, following are the important points:
myStructList.GroupBy(u => u.MailID, u=>u.ResendCount) // Incorrect grouping
myStructList.GroupBy(u => new {u.MailID, u.ResendCount }) // Correct grouping, which will do by two columns MailID and ResendCount, last one was only doing by MailID and was using ResendCount for result projection
Now the result is of type IEnumerable<IGrouping<AnonymousType,structMailJob>>, so when you do something like Select, it will end up creating Concatenated List of type IEnumerable<List<structMailJob>> (Removed the assignment to myStructList inside the Select, as that was not correct):
.Select(grp => grp.ToList())
Correct code would require you to flatten using SelectMany as follows:
newStructList = myStructList.GroupBy(u => new {u.MailID, u.ResendCount})
.SelectMany(grp => grp.ToList()).ToList();
Assign it to newStructList, but this code has little use, since literally newStructList is exactly same as myStructList post flattening, ideally you shall be able to use the grouping, so that you can get a subset and thus the correct result, however that depends on your business logic
I don't know if I got your question right but it seems to me you missed the 'Group by' signature.
List<structMailJob> myStructList = new List<structMailJob>();
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList
// .GroupBy(/*Key Selector */u => u.MailID, /*Element Selector*/u=>u.ResendCount)
.GroupBy(u => new { u.MailID, u.ResendCount }) // broup by MailID, ResendCount
// Note no Element Selector , the 'STRUCT' is 'SELECTED'
.Select(grp => {
// NOte: Key == Anonymous {MailID, ResendCount }
return grp;
})
// otherwise you get a IEnumerable<IEnumerable<T>> instead of IEnumerable<T> because you grouped it
.SelectMany(x=>x)
.ToList();
If Mrinal Kamboj's answer is what you are looking for, then you could use the following as an alternative:
var orderedList = myStructList.OrderBy(x => x.MailID).ThenBy(x => x.ResendCount);

Orchard get content by taxonomy

I'm trying to get some content by taxonomies, that are a part of that content/ Here's what I have:
var taxonomyTerms = _taxonomyService.GetTerms(taxonomy.Id).Where(t => termsToSearch.Contains(t.Name)).ToList();
var listOfTermsIds= taxonomyTerms.Select(x => x.Id).ToList();
//works well until here, I have my list of terms ids
var originalContentItems = _cm
.Query<GenericContentPart, GenericContentRecord>()
.Join<TermsPartRecord>().Where(l => !l.Terms.Select(t => t.TermRecord.Id).Except(listOfTermsIds).Any()).List();
//this returns no records
I've managed to do this with a foreach, but I want to do the same with an expression. The problem is that last bit of code doesn't return me any records.
Any help?
I found the problem:
contentItems = _cm
.Query<GenericContentPart, GenericContentRecord>()
.Join<TermsPartRecord>().ForPart<TermsPart>().List()
.Where(l => !listOfTermsIds.Except(l.Terms.Select(t => t.TermRecord.Id).ToList()).Any());
Thanks.

How to order group results in linq

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

Remove duplicates of a List, selecting by a property value in C#?

I have a list of objects that I need some duplicates removed from. We consider them duplicates if they have the same Id and prefer the one whose booleanValue is false. Here's what I have so far:
objects.GroupBy(x => x.Id).Select(x => x.Where(y => !y.booleanValue));
I've determined that GroupBy is doing no such grouping, so I don't see if any of the other functions are working. Any ideas on this? Thanks in advance.
You can do this:
var results =
from x in objects
group x by x.Id into g
select g.OrderBy(y => y.booleanValue).First();
For every Id it finds in objects, it will select the first element where booleanValue == false, or the the first one (if none of them have booleanValue == false).
If you prefer fluent syntax:
var results = objects.GroupBy(x => x.Id)
.Select(g => g.OrderBy(y => y.booleanValue).First());
Something like this should work:
var result =
objects.GroupBy(x => x.Id).Select(g =>
g.FirstOrDefault(y => !y.booleanValue) ?? g.First())
This assumes that your objects are of a reference type.
Another possibility might be to use Distinct() with a custom IEqualityComparer<>.
This partially answers the question above, but I justed need a really basic solution:
objects.GroupBy(x => x.Id)
.Select(x => x.First())
.ToArray();
The key to getting the original object from the GroupBy() is the Select() getting the First() and the ToArray() gets you an array of your objects, not a Linq object.

Categories

Resources