How to get duplicates by attribute with LINQ - c#

I have my class HomeTeam
public class HomeTeam
{
public ID { get; set; }
public string codeTeam { get; set; }
public string codeNumber { get;set; }
}
I have my list of objects:
var listaFiltrada = new List<HomeTeam>();
I want get all items that have the same codeTeam and codeNumber parameters.
Sample:
ID CodeTeam CodeNumber
1 B538 B2235
ID CodeTeam CodeNumber
2 B538 B2235
ID CodeTeam CodeNumber
3 B333 B235
I want filter list return Item with ID 1 and 2.

i suppose that the 2 linq expressions could be combined in one but i think this works
var teams = new List<HomeTeam>()
{
new HomeTeam() {ID = 1, codeTeam = "B538", codeNumber = "B2235"},
new HomeTeam() {ID = 2, codeTeam = "B538", codeNumber = "B2235"},
new HomeTeam() {ID = 3, codeTeam = "B333", codeNumber = "B235"},
new HomeTeam() {ID = 4, codeTeam = "B333", codeNumber = "B235"},
new HomeTeam() {ID = 5, codeTeam = "B333", codeNumber = "B235"},
new HomeTeam() {ID = 6, codeTeam = "B333", codeNumber = "B235333"},
new HomeTeam() {ID = 7, codeTeam = "B3444433", codeNumber = "B23534433"}
};
var groupedData =
(from t in teams
group t by new {t.codeTeam, t.codeNumber}
into grp
where grp.Count() > 1
select new
{
grp.Key.codeTeam,
grp.Key.codeNumber,
Count = grp.Count()
}).ToList();
var ids = from team in teams
join grp in groupedData on new {team.codeNumber, team.codeTeam} equals new
{grp.codeNumber, grp.codeTeam}
select new
{
ID = team.ID
};
foreach (var id in ids)
{
Console.WriteLine(id.ID);
}

this gives back a list with lists of duplicate items
var dups = listaFiltrada.Select(list => new HomeTeam()
{
ID = list.ID,
CodeTeam = list.CodeTeam,
CodeNumber = list.CodeNumber
}).GroupBy(x => new { x.CodeNumber, x.CodeTeam })
.Where(x => x.Count() > 1).ToArray();

Related

Linq group by date range

I have collection that I need to group if the parent key is common AND if the date field is within n (e.g. 2) hours of each other.
Sample data:
List<DummyObj> models = new List<DummyObj>()
{
new DummyObj { ParentKey = 1, ChildKey = 1, TheDate = DateTime.Parse("01/01/2020 00:00:00"), Name = "Single item - not grouped" },
new DummyObj { ParentKey = 2, ChildKey = 2, TheDate = DateTime.Parse("01/01/2020 01:00:00"), Name = "Should be grouped with line below" },
new DummyObj { ParentKey = 2, ChildKey = 3, TheDate = DateTime.Parse("01/01/2020 02:00:00"), Name = "Grouped with above" },
new DummyObj { ParentKey = 2, ChildKey = 4, TheDate = DateTime.Parse("01/01/2020 04:00:00"), Name = "Separate item as greater than 2 hours" },
new DummyObj { ParentKey = 2, ChildKey = 5, TheDate = DateTime.Parse("01/01/2020 05:00:00"), Name = "Grouped with above" },
new DummyObj { ParentKey = 3, ChildKey = 6, TheDate = DateTime.Parse("01/01/2020 05:00:00"), Name = "Single item - not grouped" }
};
private class DummyObj
{
public int ParentKey { set; get; }
public int ChildKey { set; get; }
public DateTime TheDate { set; get; }
public string Name { set; get; }
}
The resulting grouping should be (child keys):
{[1]}, {[2,3]}, {[4,5]}, {[6]}
I could group by parent key first then loop through comparing the individual items within the groups but hoping for a more elegant solution.
As always, thank you very much.
public static void Test()
{
var list = GetListFromDb(); //returns List<DummyObj>;
var sortedList = new List<DummyObj>();
foreach(var g in list.GroupBy(x => x.ParentKey))
{
if(g.Count() < 2)
{
sortedList.Add(g.First());
}
else
{
var datesInGroup = g.Select(x => x.TheDate);
var hoursDiff = (datesInGroup.Max() - datesInGroup.Min()).TotalHours;
if(hoursDiff <= 2)
{
string combinedName = string.Join("; ", g.Select(x => x.Name));
g.First().Name = combinedName;
sortedList.Add(g.First());
}
else
{
//now it's the mess
DateTime earliest = g.Select(x => x.TheDate).Min();
var subGroup = new List<DummyObj>();
foreach(var line in g)
{
if((line.TheDate - earliest).TotalHours > 2)
{
//add the current subgroup entry to the sorted group
subGroup.First().Name = string.Join("; ", subGroup.Select(x => x.Name));
sortedList.Add(subGroup.First());
//new group needed and new earliest date to start the group
sortedList = new List<DummyObj>();
sortedList.Add(line);
earliest = line.TheDate;
}
else
{
subGroup.Add(line);
}
}
//add final sub group, i.e. when there's none that are over 2 hours apart or the last sub group
if(subGroup.Count > 1)
{
subGroup.First().Name = string.Join("; ", subGroup.Select(x => x.Name));
sortedList.Add(subGroup.First());
}
else if(subGroup.Count == 1)
{
sortedList.Add(subGroup.First());
}
}
}
}
}
Here you go:
List<DummyObj> models = new List<DummyObj>()
{
new DummyObj { ParentKey = 1, ChildKey = 1, TheDate = DateTime.Parse("01/01/2020 00:00:00"), Name = "Single item - not grouped" },
new DummyObj { ParentKey = 2, ChildKey = 2, TheDate = DateTime.Parse("01/01/2020 01:00:00"), Name = "Should be grouped with line below" },
new DummyObj { ParentKey = 2, ChildKey = 3, TheDate = DateTime.Parse("01/01/2020 02:00:00"), Name = "Grouped with above" },
new DummyObj { ParentKey = 2, ChildKey = 4, TheDate = DateTime.Parse("01/01/2020 04:00:00"), Name = "Separate item as greater than 2 hours" },
new DummyObj { ParentKey = 2, ChildKey = 5, TheDate = DateTime.Parse("01/01/2020 05:00:00"), Name = "Grouped with above" },
new DummyObj { ParentKey = 3, ChildKey = 6, TheDate = DateTime.Parse("01/01/2020 05:00:00"), Name = "Single item - not grouped" }
};
List<List<DummyObj>> groups =
models
.GroupBy(x => x.ParentKey)
.Select(xs => xs.OrderBy(x => x.TheDate).ToList())
.SelectMany(xs => xs.Skip(1).Aggregate(new[] { xs.Take(1).ToList() }.ToList(), (a, x) =>
{
if (x.TheDate.Subtract(a.Last().Last().TheDate).TotalHours < 2.0)
{
a.Last().Add(x);
}
else
{
a.Add(new [] { x }.ToList());
}
return a;
}))
.ToList();
string output =
String.Join(", ",
groups.Select(x =>
$"{{[{String.Join(",", x.Select(y => $"{y.ChildKey}"))}]}}"));
That gives me:
{[1]}, {[2,3]}, {[4,5]}, {[6]}

Use LINQ to Return a List of Items that Match an ID in a List inside a List of Items

I have a list of items like so:
Item item = new Item {ID = 1, Name = 'One'};
Item item2 = new Item {ID = 2, Name = 'Two'};
List<Item> items = new List<Item>;
items.Add(item);
items.Add(item2);
Then I have another list of other items, with a list inside that is a list of associations to the first item list:
OtherItem otherItem = new OtherItem {ID = 1, Name = 'OtherOne', ListOfItems = {1,2}};
OtherItem otherItem2 = new OtherItem {ID = 2, Name = 'OtherTwo', , ListOfItems = {2}};
List<OtherItem> otherItems = new List<OtherItem>;
otherItems.Add(otherItem);
otherItems.Add(otherItem2);
Now, I want to return a list of Item that are included in the OtherItem ListOfItems. FOr example's sake, how would I return a list of Item where OtherItem's ListOfItems includes ID: 1?
items.Where(i => otherItems.Any(oi => oi.ListOfItems.Any(li => li == i.ID)))
You can use Where:
List<OtherItem> otherItems = new List<OtherItem>
{
new OtherItem { ID = 1, Name = "OtherOne", ListOfItems = { 1, 2 } },
new OtherItem { ID = 2, Name = "OtherTwo", ListOfItems = { 2 } }
};
var ID = 1;
var result = otherItems.Where(s => s.ListOfItems.Any(x => x == ID))
.Select(s => new { s.ID, s.Name });

how can I write this linq group by as a non-query expression?

I have a collection of group users which has a GroupId and UserId. I need to filter out any duplicate GroupId/UserId objects which may exist in the collection. How can I write a non-query expression GroupBy to filter out the duplicate rows? The following example is adapted from a group by example that I found online but I'm not quite clear on how to refine this code for my particular scenario:
var groupByResults =
groupUsers.GroupBy(
x => x.GroupId,
x => x.UserId,
(key, g) => new
{
[?] = key,
[?] = g.ToList()
}
);
If your data looks like the list below you can group by the compound key then take the first value in each group. The OrderBy is optional
var groupUsers = new List<dynamic>() {
new { groupId = 1, userId = 1, name = "a" },
new { groupId = 1, userId = 1, name = "b" },
new { groupId = 1, userId = 2, name = "c" }
};
var result = groupUsers
.GroupBy(u => new { u.groupId, u.userId} )
.Select(g => g.OrderBy(u => u.name).FirstOrDefault());
To find out the duplicated userId, groupId.
GroupBy userId, groupId
Count if any group item >=2
SelectMany the collection
Code:
var duplicatedUsers = groupUsers
.GroupBy(gu => new { gu.UserId, gu.GroupId })
.Where(g => g.Count() >= 2)
.SelectMany(g => g)
Following code will be helpful to you,
class GroupUsers
{
public int GroupId {get;set;}
public int UserId {get;set;}
}
public class Program
{
public static void Main()
{
var groupUsers = new List<GroupUsers>() {
new GroupUsers{ GroupId = 1, UserId = 1},
new GroupUsers{ GroupId = 1, UserId = 1},
new GroupUsers{ GroupId = 1, UserId = 2},
new GroupUsers{ GroupId = 1, UserId = 2},
new GroupUsers{ GroupId = 1, UserId = 3},
new GroupUsers{ GroupId = 1, UserId = 4},
new GroupUsers{ GroupId = 1, UserId = 5},
new GroupUsers{ GroupId = 1, UserId = 3}
};
var result1 = groupUsers
.GroupBy(u => new { u.GroupId, u.UserId} )
.Where(g => g.Count()>=2) // check for duplicate value by checking whether the count is greater than or equal to 2.
.SelectMany(g=>g); // flatten the list
foreach(var user in result1) // Iterate over the result
{
Console.WriteLine(user.GroupId +" "+user.UserId);
}
// Or
var result2 = from a in groupUsers
group a by new{a.GroupId, a.UserId} into grp
where grp.Count()>=2
from g in grp select new{g}
foreach(var user in result2)
{
Console.WriteLine(user.g.GroupId +" "+user.g.UserId);
}
}
}

Grouping troubles in LINQ

I have some list of structures like this:
struct va_data
{
public int item_id;
public int type_id;
public double item_value;
public DateTime value_date;
}
I trying group the list by type_id and take items where value_date is maximum then group by item_id and take items only where item_value is minimum
There is my syntax
from x in dataList
group x by x.type_id into grouped
select grouped.Where(x => x.value_date == grouped.Max(y => y.value_date))
.GroupBy(x => x.item_id) // and here i was stuck.
Example
var dataList = new []
{
new va_data {item_id = 1, type_id = 1, item_value = 0, value_date = "2013.07.29"},
new va_data {item_id = 1, type_id = 1, item_value = 1, value_date = "2013.07.30"},
new va_data {item_id = 2, type_id = 1, item_value = 0, value_date = "2013.07.29"},
new va_data {item_id = 2, type_id = 1, item_value = 1, value_date = "2013.07.29"},
new va_data {item_id = 4, type_id = 2, item_value = 5, value_date = "2013.07.29"},
new va_data {item_id = 4, type_id = 3, item_value = 9, value_date = "2013.07.30"},
};
The result must be
var dataListResult = new []
{
new va_data {item_id = 1, type_id = 1, item_value = 1, value_date = "2013.07.30"},
new va_data {item_id = 2, type_id = 1, item_value = 0, value_date = "2013.07.29"},
new va_data {item_id = 4, type_id = 2, item_value = 5, value_date = "2013.07.29"},
}
Given the following class
class va_data
{
public int item_id;
public int type_id;
public double item_value;
public DateTime value_date;
}
and your example data, you can use a query like this:
from data in dataList
group data by new {data.item_id, data.type_id} into g
let max_value_date = g.Max(x => x.value_date)
from i in g.Where(x => x.value_date == max_value_date)
group i by i.item_id into g2
let min_item_value = g2.Min(x => x.item_value)
from x in g2
where x.item_value == min_item_value
select x;
to get the following result:
Just split your query into two parts - getting latest of each type, and then getting minimal of each item:
var latestOfEachType =
from d in dataList
group d by d.type_id into typeGroup
select typeGroup.OrderByDescending(x => x.value_date).First();
var result = from d in latestOfEachType
group d by d.item_id into itemGroup
select itemGroup.OrderBy(x => x.item_value).First();
This query will be executed as single query. But in this case it looks much more readable to me. Also don't use mutable structs!. Use classes instead.
EDIT: Thus you have several items for max date, then query needs two small tweaks - select all items where date is max, and use SelectMany to iterate over them:
var latestOfEachType =
from d in dataList
group d by d.type_id into typeGroup
let maxDate = typeGroup.Max(x => x.value_date)
select typeGroup.Where(x => x.value_date == maxDate);
var result = from d in latestOfEachType.SelectMany(g => g)
group d by d.item_id into itemGroup
select itemGroup.OrderBy(x => x.item_value).First();

How to group by in LINQ?

I need to return the last 30 days of a speciefic user daily appointments and check if the user made at least 8 hours of appointments for each day.
in sql i can do that with this command:
select IDAppointment,IDUser, SUM(DurationInHours) from Note where AppointmentDate > *lastmonth and IDUser = #userID group by IDUser,IDAppointment,AppointmentDate
and after that i get the result and validate the DurationInHours(double type).
Is it possible to do it using LINQ?
Get the list of the last month user appointments and validate it day by day
Thanks!
This should be roughly there although this is off the top of my head as not at an IDE.
var result = context.Notes
.Where(n => [Your where clause])
.GroupBy(n => new { n.IDUser, n.IDAppointment, n.AppointmentDate})
.Select(g => new {
g.Key.IDAppointment,
g.Key.IDUser,
g.Sum(n => n.DurationInHours)});
UPDATE:
For reference your where clause will be something like this... (again off the top of my head)
DateTime lastMonth = DateTime.Today.AddMonths(-1);
int userId = 1 // TODO: FIX
var result = context.Notes.Where(n => n.AppointmentDate > lastMonth
&& n.IDUser = userId)
Resulting in....
DateTime lastMonth = DateTime.Today.AddMonths(-1);
int userId = 1 // TODO: FIX
var result = context.Notes
.Where(n => n.AppointmentDate > lastMonth
&& n.IDUser = userId)
.GroupBy(n => new { n.IDUser, n.IDAppointment, n.AppointmentDate})
.Select(g => new {
g.Key.IDAppointment,
g.Key.IDUser,
g.Sum(n => n.DurationInHours)});
Here is a solution which I tested.
DateTime lastMonth = DateTime.Today.AddMonths(-1);
int selectedUserId = 2;
var notes = new List<Note>(
new Note[] {
new Note() {
AppointmentDate = new DateTime(2013,7,30){},
IDAppointment = 1, IDUser = 1, DurationInHours = 1
},
new Note() {
AppointmentDate = new DateTime(2013,7,30){},
IDAppointment = 1, IDUser = 1, DurationInHours = 2
},
new Note() {
AppointmentDate = new DateTime(2013,7,30){},
IDAppointment = 1, IDUser = 1, DurationInHours = 3
},
new Note() {
AppointmentDate = new DateTime(2013,7,28){},
IDAppointment = 2, IDUser = 2, DurationInHours = 2
},
new Note() {
AppointmentDate = new DateTime(2013,7,28){},
IDAppointment = 2, IDUser = 2, DurationInHours = 3
},
new Note() {
AppointmentDate = new DateTime(2013,7,27){},
IDAppointment = 2, IDUser = 2, DurationI nHours = 4
},
new Note() {
AppointmentDate = new DateTime(2013,7,26){},
IDAppointment = 3, IDUser = 3, DurationInHours = 3
},
new Note() {
AppointmentDate = new DateTime(2013,7,25){},
IDAppointment = 3, IDUser = 3, DurationInHours = 4
},
new Note() {
AppointmentDate = new DateTime(2013,7,24){},
IDAppointment = 3, IDUser = 3, DurationInHours = 5
}
}
);
var results = from n in notes
group n by new {n.IDUser, n.IDAppointment, n.AppointmentDate}
into g
where g.Key.AppointmentDate > lastMonth &&
g.Key.IDUser == selectedUserId
select new {
g.Key.IDAppointment,
g.Key.IDUser,
TotalHours = g.Sum(n => n.DurationInHours)
};
The summation property needed to be given a name explicitly (i.e. TotalHours) or else you get error CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.

Categories

Resources