C # and LINQ-group based on their IDs and then find common values ββin them
I have a collection of objects like the following.
List<Product> productList=new List<Product>();
The structure of the Product class looks like this.
public class Product
{
public int Id;
public Product(int id)
{
Id=id;
}
public List<SomeOtherList> t;
}
Class Product contains a different list
public class SomeOtherList
{
private int value;
public SomeOtherList(int val)
{
value=val;
}
}
Product prodOne=new Product(1);
List<SomeOtherList> temp_1=new List<SomeOtherList>();
temp_1.Add(new SomeOtherList(10));
temp_1.Add(new SomeOtherList(20));
temp_1.Add(new SomeOtherList(30));
prodOne.t=temp_1;
Product prodTwo=new Product(2);
List<SomeOtherList> temp_2=new List<SomeOtherList>();
temp_2.Add(new SomeOtherList(100));
temp_2.Add(new SomeOtherList(40));
temp_2.Add(new SomeOtherList(30));
prodTwo.t=temp_2;
Product prodThree=new Product(1);
List<SomeOtherList> temp_3=new List<SomeOtherList>();
temp_3.Add(new SomeOtherList(10));
temp_3.Add(new SomeOtherList(20));
prodThree.t=temp_3;
productList.Add(prodOne);
productList.Add(prodTwo);
productList.Add(prodThree);
I want to get "SomeOtherList" objects common to all products. For example, for Product ID 1, I should get a SomeOtherList 20 object as normal.
For this I wrote the following LINQ query.
List<Product> t=productList
.GroupBy(p => p.Id)
.Where(g => g.Count() == productList.Count)
.Select(x => x.First())
.ToList();
But that doesn't give me what I want. Can someone point out what is wrong with this request?
source to share
You want intersection between yours SomeOtherList
when the product has a specific value (i.e. 1).
So, you need to first select each Product
one that has the correct Id
one and then combine them SomeOtherList
together and group by value.
To do this, we need to flatten each SomeOtherList
for a Product
, which we can do withSelectMany
Projects each element of a sequence to an IEnumerable and aligns the resulting sequences into a single sequence.
Single identifier
If we are only interested in one Id, we can do the following
var common1 = productList
.Where(product => product.Id == 1)
.SelectMany(product => product.t)
.GroupBy(groupValue => groupValue.value)
.Where(groupValue => groupValue.Count() > 1)
.Select(values => values.First());
It will be:
- Filter products based on Id equal to 1
- Align each product
SomeOtherList
into one IEnumerable - Group each element based on
SomeOtherList.value
- Filter out the groups that only have 1 entry as we only want the ones that are general.
All identifiers
If, however, we would like to get a list of all the duplicates for each key, then we can do the same as for a single identifier, but we have the first step in which we group based on the identifier.
var common = productList
.GroupBy(groupId => groupId.Id)
.Select(groupId => groupId.SelectMany(product => product.t)
.GroupBy(groupValue => groupValue.value)
.Where(groupValue => groupValue.Count() > 1)
.Select(values => values.First().value));
source to share
Difficult to do in one LINQ query, but the following code should work.
var min = productList.Min(x=>x.Id);
var max = productList.Max(x=>x.Id);
for(int i = min; i<=max;i++)
{
var products = productList.Where(x=>x.Id = i).ToList();
List<SomeOtherList> cList = new List<SomeOtherList>();
foreach(var product in products)
{
cList.AddRange(product.t);
}
var distinctList = cList.Distinct();
}
source to share
Because of nested lists, you need to group the expanding of the SomeOtherList with SelectMany before grouping. I think this is what you need.
var result = productList
.GroupBy(p => p.Id)
.Select(pg =>
new
{
Id = pg.Key,
CommonOtherList = pg
.SelectMany(solg => solg.t)
.GroupBy(solg => solg.value)
.Where(solg => solg.Count() == pg.Count())
.Select(solg => new { OtherId = solg.Key })
});
foreach (var product in result)
{
Console.WriteLine(product.Id);
Console.WriteLine("**Common**");
foreach (var otherProduct in product.CommonOtherList)
{
Console.WriteLine("-->{0}", otherProduct.OtherId);
}
}
source to share