C# LINQ getting duplicates from a list - c#

I have a list of objects that have a string, and int and another int.
I want to be able to create a list of all the objects that have a duplicate string.
Here is what I have so far:
MyObject duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
The error I am getting is that I cannot implicitly convert the type System.Collections.Generic.List<string, MyObject> to MyObject

var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g=>g)
.ToList();

you need to write
List<MyObject> duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();

You could use ToLookup to make a nice data structure with all the info you need
var objectsByString = allMyObjects.ToLookup(o => o.MyString);
This will return a Lookup<string, MyObject>. You can get the duplicate strings using:
var duplicateStrings = objectsByString.Where(l => l.Count()>1).Select(l => l.Key);
which will return a IEnumerable<string> with the duplicate strings. And, for each duplicate you can access the actual objects that have duplicates using something like this:
string duplicateKey = duplicateStrings.First();
var duplicateObjects = objectsByString[duplicateKey]
which returns a IEnumerable<MyObject> with the items that have that string.

There are several problem, the first is a List-of-MyObject cannot be assigned to MyObject, so let's use var to ignore this for a second.
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
Now, the type of duplicates is List<IGrouping<string, MyObject>> (despite the incorrectly reported error message). Whoops, gotta get rid of (or write to code to account for) the groups!
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g)
.ToList();
Now the type of duplicates is List<MyObject>, after having selected every ("selected many") object from every group with more than one item. Better, but this still isn't an MyObject. Well, that's fine: fix the declared type of the variable (that var was previously automatically doing)..
List<MyObject> duplicates = /* same as before */;
Or leave var to do it's thing and if an IEnumerable<MyObject> is fine, simply omit the ToList:
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g);
Go forth and iterate thy duplicates!

Related

Cannot access groupBy data Linq C#

Inside method I have a list that contains grouped data:
var listofData = _context.DBONE.where(x => x.Id==3 && x.Status!=0)
.GroupBy(x => new { x.Name, x.Class })
.Select(q => new { Result = q.ToList() }).ToList();
if (methodParam == 10)
{
data = listofData.Where(x => FunctionCheck(---CANNOT ACCESS THE FIELDS FROM GROUP DATA TO PASS AS PARAMETERS---) == 10).ToList();
}
And this is the function that will receive 2 parameter from the grouped data:
private int FunctionCheck(int id, string name)
{...}
But, I cannot access the desired field inside 'listofData'. I can access only in case the listofData is not using groupBy().
If I understand what you are trying to do correctly, you are able to access your data but you are actually creating a "list of a list"
Watch my example, I think I have reproduced your scenario here:
As you can see, I then have a "result" which contains a list of users where Id == 3. The problem is that you create a new anonymous object with a props that is a list. So if you try the last thing you see in my image above, I think you will be able to access your rows.
The reason is that after your GroupBy call, the result is of a grouping type - every item of your list is an Enumerable of the original item, so you would have to operate on that grouping in a following manner:
// Groups such that all items in that group pass your check
listofData
.Where(group => group.All(item => FunctionCheck(item.Id, item.Name) == 10))
.ToList();
// Groups where at least one item matches
listofData
.Where(group => group.Any(item => FunctionCheck(item.Id, item.Name) == 10))
.ToList();
The desired outcome is not really clear from the question but this is the step you are likely missing.
Another approach which might be potentially useful is pre-filter the colleciton of items before grouping them:
var listOfGroupedDatas = _context.DBONE
.Where(x => x.Id ==3 && x.Status != 0 && FunctionCheck(item.Id, item.Name) == 10)
.GroupBy(x => new { x.Name, x.Class })
.ToList();
// This will result in a list of groupings in which all items pass your check
I think you want to call SelectMany to project into one dimensional array.
var listofData = _context.DBONE.where(x => x.Id==3 && x.Status!=0)
.GroupBy(x => new { x.Name, x.Class })
.SelectMany(q => q.ToList()).ToList();

Dynamic linq query that Contains ALL from another list

I want to be able to find all orders with items that contain BOTH apples and oranges that I have in a list.
var itemToFind = new List<string>()
{
"apples",
"cookies"
};
How can I rewrite this so that Contains is dynamic?
This returns what I want but how do I make it loop through my list so that it is dynamic?
var query = result
.Where(o => o.OrderItems
.Any(i => i.Item.Name.Contains("apples")))
.Select(x => x)
.Where(y => y.OrderItems
.Any(b => b.Item.Name.Contains("cookies"))).ToList();
// returns 2 orders
Try something like this:
result.Where(o => o.OrderItems.Any(i => itemToFind.All(itf => i.Item.Name.Contains(itf)))).ToList()
This seems to work but not sure if that is the best way.
foreach (var item in listFacets)
{
// append where clause within loop
result = result
.Where(r => r.RecipeFacets
.Any(f => f.Facet.Slug.Contains(item)));
}

How can I split a List<T> into two lists, one containing all duplicate values and the other containing the remainder?

I have a basic class for an Account (other properties removed for brevity):
public class Account
{
public string Email { get; set; }
}
I have a List<T> of these accounts.
I can remove duplicates based on the e-mail address easily:
var uniques = list.GroupBy(x => x.Email).Select(x => x.First()).ToList();
The list named 'uniques' now contains only one of each account based on e-mail address, any that were duplicates were discarded.
I want to do something a little different and split the list into two.
One list will contain only 'true' unique values, the other list will contain all duplicates.
For example the following list of Account e-mails:
unique#email.com
dupe#email.com
dupe#email.com
Would be split into two lists:
Unique
unique#email.com
Duplicates
dupe#email.com
dupe#email.com
I have been able to achieve this already by creating a list of unique values using the example at the top. I then use .Except() on the original list to get the differences which are the duplicates. Lastly I can loop over each duplicate to 'pop' it out of the unique list and move it to the duplicate list.
Here is a working example on .NET Fiddle
Can I split the list in a more efficient or syntactically sugary way?
I'd be happy to use a third party library if necessary but I'd rather just stick to pure LINQ.
I'm aware of CodeReview but feel the question also fits here.
var groups = list.GroupBy(x => x.Email)
.GroupBy(g => g.Count() == 1 ? 0 : 1)
.OrderBy(g => g.Key)
.Select(g => g.SelectMany(x => x))
.ToList();
groups[0] will be the unique ones and group[1] will be the non-unique ones.
var duplicates = list.GroupBy(x => x) // or x.Property if you are grouping by some property.
.Where(g => g.Count() > 1)
.SelectMany(g => g);
var uniques = list.GroupBy(x => x) // or x.Property if you are grouping by some property.
.Where(g => g.Count() == 1)
.SelectMany(g => g);
Alternatively, once you get one list, you can get the other one using Except:
var uniques = list.Except(duplicates);
// or
var duplicates = list.Except(uniques);
Another way to do it would be to get uniques, and then for duplicates simply get the elements in the original list that aren't in uniques.
IEnumerable<Account> uniques;
IEnumerable<Account> dupes;
dupes = list.Where(d =>
!(uniques = list.GroupBy(x => x.Email)
.Where(g => g.Count() == 1)
.SelectMany(u => u))
.Contains(d));

LINQ - grouping by name to Dictionary<string, List<T>>

I am building a library app. I have a list of Books where some of them have a duplicate name (there are few copies of the same book). I would like to convert the list to Dictionary>, where the string would be the name of a book, and the List would contain all the Book objects with this name.
I've managed to get this far:
var result = queriedBooks
.GroupBy(b => b.Name)
.Where(g => g.Count() >= 1)
.ToDictionary(b => b.Key, /// );
This is where I get stuck. I have no idea what to pass as a value. Intellisense does not help either, as there is no Value property available. I would like to avoid using anonymous objects, as each Book entry has many properties which I use in a view.
Thank you very much!
As an alternative you may want just Lookup<String, Book> instead of combersome Dictionary<String, List<Book>>:
LookUp<String, Book> result = queriedBooks
.ToLookup(book => book.Name);
In case of Dictionary<String, List<Book>>:
var result = queriedBooks
.GroupBy(book => book.Name)
.ToDictionary(chunk => chunk.Key, chunk => chunk.ToList());
Please note that .Where(g => g.Count() >= 1) is redundant;
You should simply use ToList() like this:
.ToDictionary(b => b.Key, b => b.ToList());
Each group has a Key property which is the key. It also (the group) is an IEnumerable<Book> that represents the items in the group which is why ToList() works.
try with ToList with distinct for example In a table, a column may contain many duplicate values; and sometimes you only want to list the different (distinct) values.
var query = queriedBooks
.Distinct()
.GroupBy(b => b.Name)
.ToDictionary(b => b.Key, b.ToList() );
please don't use it .Where(g => g.Count() >= 1) it is redundant

Getting Row Index for LINQ Query using Custom Object

I have the following LINQ query to receive indexes:
fieldIndexes = this.record.Fields.Where(a => !a.IsCodeField)
.OrderBy(a => a.DatabaseIndex)
.Select(a => a.DatabaseIndex - 1)
.ToArray();
But I want to replace the a.DatabaseIndex with the actual index of the search. I am aware of the syntax .Select((a, index) => new (index, a))... but I am not sure how to cast the a here to be of my type which in this case is Field. I have tried:
fieldIndexes = this.record.Fields.Select((a, index) => new {index, a})
.Where(a => !a.IsCodeField) // <- Invalid Cast.
.OrderBy(a => a.DatabaseIndex)
.Select(a => a.DatabaseIndex - 1)
.ToArray();
How can I cast a to my type within the LINQ statement?
Thanks for your time.
In the Where clause you are working with your newly created anonymous objects with properties a and index, which you can use:
.Where(a => !a.a.IsCodeField)
Of course this can be done in more readable fasion:
fieldIndexes = this.record.Fields.Select((a, index) => new {Index = index, Field = a})
.Where(a => !a.Field.IsCodeField)
...
You are projecting sequence items to anonymous objects with properties index and a. Original item will be accessible via property a:
fieldIndexes = this.record.Fields.Select((a, index) => new {index, a})
.Where(x => !x.a.IsCodeField)

Categories

Resources