Linq group objects by unique values and make two lists - c#

I have an array of objects with property Number inside them. I need to group them by values i.e. objects contain those sample values:
1 2 3 3 3 4 5 6 6 6 7 7
I have to group them like this:
listOfUniqe = {1,2,4,5}
listOfDuplicates1 = {3,3,3}
listOfDuplicates2 = {6,6,6}
listOfDuplicates3 = {7,7}
...
I tried to use distinct, with First(). But this distincts me first occurences and remove duplicates. I want to erase also first occurence of object if it had duplicates and move them to another list.
List<Reports> distinct = new List<Reports>;
distinct = ArrayOfObjects.GroupBy(p => p.Number).Select(g => g.First()).ToList();
Any ideas how I could do this?

To get groups with just one element use that:
distinct = ArrayOfObjects.GroupBy(p => p.Number)
.Where(g => g.Count() == 1)
.ToList();
And to get list of groups with more elements use that:
nonDistinct = ArrayOfObjects.GroupBy(p => p.Number)
.Where(g => g.Count() > 1)
.Select(g => g.ToList())
.ToList();

First group the items:
var groups = values.GroupBy(p => p.Number).ToList();
The unique ones are the ones with a group count of one:
var unique = groups.Where(g => g.Count() == 1).Select(g => g.Single()).ToList();
The ones with duplicates are the other ones:
var nonUnique = groups.Where(g => g.Count() > 1).ToList();

Related

Filter a List of Strings and select only one occurance of a string with multiple occurance

var paymentTypes = _context
.BursaryTransactions
.Select(c => c.PaymentType)
.ToList();
string[] obj = paymentTypes
.ToArray();
var a = obj[1];
The first line retrieves a list of Payment type which are in strings from BursaryTransactions Table
the second line converts the list to array.
The list from the first line however contains Similar Strings for example
Post Utme
School Fee
School Fee
Post Utme
Hnd Form
Hnd Form
I want to filter these list and retrive just one occurrance of an item that appears more than once. then converts the new list to array.
You can try GroupBy and choose groups with more than 1 item:
var result = _context
.BursaryTransactions
.GroupBy(c => c.PaymentType) // Group By PaymentType
.Where(group => group.Count() > 1) // groups with more than 1 item
.Select(group => group.First()) // we want just the 1st item of such group
.ToList(); // materialized as a List<T>
Edit: to remove duplicates we can take First item from each group:
var result = _context
.BursaryTransactions
.GroupBy(c => c.PaymentType)
.Select(group => group.First()) // First().PaymentType if you want PaymentType only
.ToList();
You can try this way
var result = _context.BursaryTransactions.GroupBy(c => c.PaymentType)
.Where(g => g.Count() > 1)
.Select(g => g.First())
.ToArray();

Using Linq to get maximum value of only duplicates

I am trying to use Linq to return me all of the items in my object that have a specific property value duplicated, and the maximum value of another specific property for all of the duplicates.
My object has properties CourseInfoId which is how I want to check for duplicates and a property Priority which i want the maximum value (and lots of other properties).
I thought this would work, but it's giving me every item in the object.
var group = from a in r
group a by a.CourseInfoId into b
let maxPriority = b.Max(d => d.Priority)
where b.Skip(1).Any()
from c in b
where c.Priority == maxPriority
select c;
Where am I going wrong?
What you'll want to do is group by CourseInfoId, then filter on groups that have more than 1 item which will get you all of the duplicate items. Next, you'll have to flatten out the groups again and get the maximum property value from the results.
var maxPriority = items
.GroupBy(i => i.CourseInfoId)
.Where(g => g.Count() > 1)
.SelectMany(g => g)
.Max(i => i.Priority);
EDIT: I see now that you only want to check the properties of the duplicates, not all of the items with a duplicate ID. All you have to do is skip the first item of each group in the .SelectMany() call:
var maxPriority = items
.GroupBy(i => i.CourseInfoId)
.Where(g => g.Count() > 1)
.SelectMany(g => g.Skip(1))
.Max(i => i.Priority);

Determine Duplicate data using LINQ to EF

I have a dataset that i want to groupby to determine duplicate data.
Example i have a dataset that looks like this.
|id | Number | ContactID
1 1234 5
2 9873 6
3 1234 7
4 9873 6
Now i want to select data that has more than one occurrence of Number but only if the ContactID is not the same.
So basically return
| Number | Count |
1234 2
Any help would be appreciated using LINQ to EF, thanks.
Update:
All thanks to #DrCopyPaste, as he told me that I misunderstood your problem. Here is the correct solution:-
var result = from c in db.list
group c by c.Number into g
let count = g.GroupBy(x => x.ContactID).Where(x => x.Count() == 1).Count()
where count != 0
select new
{
Number = g.Key,
Count = count
};
Sample Fiddle.
This query avoids making a custom IEqualityComparer as if I remember correctly don't think they play well with EF.
var results = data.GroupBy(number => number.Number)
.Where(number => number.Count() > 1)
.Select(number => new
{
Number = number.Key,
Count = number.GroupBy(contactId => contactId.ContactId).Count(x => x.Count() == 1)
})
.Where(x => x.Count > 0).ToList();
Fiddle
It does an initial GroupBy to get all Numbers that are duplicated. It then selects a new type that contains the number and a second GroupBy that groups by ContactId then counts all groups with exactly one entry. Then it takes all results whose count is greater than zero.
Have not testing it against EF, but the query uses only standard Linq operators so EF shouldn't have any issues translating it.
Another way of doing this(using 1 level of grouping):
var results = data
.Where(x => data.Any(y => y.Id != x.Id && y.Number == x.Number && y.ContactId != x.ContactId))
.GroupBy(x => x.Number)
.Select(grp => new { Number = grp.Key, Count = grp.Count() })
.ToList();
Fiddle

How to filter List With LINQ C#

I need to filter a List<Students> into StudentsWitHighestDebts.
The criteria is that only students where ZachetDidNotPass has maximum value and maximum-1 in all List<Students> are included in the result.
var StudentsWitHighestDebts = students
.Where(s => s.ZachetDidNotPass.(some condition))
.OrderBy(s => s.Name)
.ToList();
For example, given a list of students that have ZachetDidNotPass values 0 1 2 5 6 7. The resulting StudentsWitHighestDebts should only contain the students with 7 and 6 values in ZachetDidNotPass.
First option: take 2 highest debts and filter students by ZachetDidNotPass:
var highestDebts = students.Select(s => s.ZachetDidNotPass)
.OrderByDescending(p => p).Take(2).ToArray();
var studentsWitHighestDebts = students
.Where(s => highestDebts.Contains(s.ZachetDidNotPass))
.OrderByDescending(s => s.ZachetDidNotPass).ToList();
Second option - group by ZachetDidNotPass, sort groups by key descending, take top 2 groups and select students from groups
var studentsWitHighestDebts = students.GroupBy(s => s.ZachetDidNotPass)
.OrderByDescending(g => g.Key).Take(2)
.SelectMany(g => g).ToList();
And third option (take students with highest debt and highestDebt - 1)
var highestDebt = students.Max(s => s.ZachetDidNotPass);
var studentsWitHighestDebts = students
.Where(s => s.ZachetDidNotPass == highestDebt || s.ZachetDidNotPass == highestDebt - 1)
.OrderByDescending(s => s.ZachetDidNotPass).ToList();

Selecting distinct with certain condition

I have values in a list:
List1
ID groupID testNo
1 123 0
2 653 1
3 776 6
4 653 0
I want to write a T-Sql or linq or lambda expression, so that whenever there is a duplicate it should pick the one with value !=0
I am using this expression but it is basically not giving the results I want.
var list2 = list1.GroupBy(x => x.testNo).Select(y => y.First());
How can I get the results so groupID 653 is chosen with testNo 1 with rest of the records?
There are a few approaches you could take. I don't know if any of them are full proof. One would be to do an OrderBy on testNo so that items with a non zero testNo will come up before those with 0.
var list2 = list1.Orderby(y => y.testNo).GroupBy(x => x.testNo).Select(z => z.FirstOrDefault());
If you can guarantee that testNo = 0 only occurs for dupes then the easiest way is just to use a where.
var list2 = list1.Where(x => x.testNo > 0).First();
This should give you the desired results:
var list2 = list1.GroupBy(x => x.groupID)
.Select(x => list1.Single(item => item.groupID == x.Key
&& item.testNo == x.Max(y => y.testNo)))
.ToList();
Basically, group by groupID and then select each item from the original list1 that matches the distinct groupID and has the max value for testNo for that groupID.
var result = list.GroupBy(x => x.groupID).Select(g => g.Count() == 1 ? g.First() : g.First(x => x.testNo != 0));

Categories