Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# find all matching Items from a List<Item>

Tags:

c#

linq

Let's say we have a List<Product> and each product item in the list has many List<Type>

public class Product{
public int Id {get:set;}
public string Name {get:set;}
public List<Type> Types {get;set;}
}

public class Type{
public int Id {get;set;}
public string Name{get;set;}
}

After I create a product list, I need to group them by the type and then find all the belongs to each type. I think I should try LINQ for this. Here is what I have done so far, but seems not the right way to get the job done. May be someone can help me.

var productsList = new List<Product>();
//Adding products and types for each of them

var productTypesList = new Dictionary<int, string>();

 foreach (var p in productsList)
                            {
                                var pTypes = p.Types;
                                foreach (var ptype in
                                    pTypes.Where(x=> !productTypesList .ContainsKey(x.Id)))
                                {
                                    productTypesList.Add(ptype.Id, ptype.Name);
                                }
                            }

Then I'm trying to search Like this

foreach (var t in productTypesList)
{
var matches = productsList.FindAll(........); 
// from here I need to find all the product which belongs to type (t.id)

if (matches.Count > 0)
{
//Do somthing here
}
}
like image 699
randika Avatar asked Jul 03 '11 08:07

randika


2 Answers

The following does what you want:

var productsPerType =
    from t in products.SelectMany(
        p => p.Types, (p, t) => new { Product = p, TypeId = t.Id })
    group t by t.TypeId
    into g
    select new { g.Key, Products = g.Select(x => x.Product) };

First, you do a SelectMany to get a list of all types inside the products. For each type you remember the type id and the corresponding product:

from t in products.SelectMany(
    p => p.Types, (p, t) => new { Product = p, TypeId = t.Id })

Each t is now an anonymous object containing a type id and a product. Next, you group these objects by type id. Now we have a group of products for each type id.

To give you an example, suppose you have the following products and types:

Product A -- Types 1, 2, 3
Product B -- Types 1
Product C -- Types 1, 3

The SelectMany gives the following intermediate result:

1, A
2, A
3, A
1, B
1, C
3, C

We group this result by type id so we get the following groups:

1, { A, B, C }
2, { A }
3, { A, C }

And this is the result you wanted.

like image 156
Ronald Wildenberg Avatar answered Oct 14 '22 11:10

Ronald Wildenberg


   var types = (from p in productsList
               from t in p.Types
               select t).Distinct(new TypeComparerById());
   var productsGrouped = (from t in types
                         select new 
                         {
                          Type = t,
                          ProductsPerType = productsList.Where(p=>p.Types.Any(pt=>pt.Id == t.Id))
                         }).ToList();

Edit
Ronald Wildenberg has correctly pointed that the call on Distinct() would work only if the instances are the same. To correct this I update with the following implementation

public class TypeComparerById : IEqualityComparer<Type>
{
    public bool Equals(Type t1, Type t2)
    {
        if (t1.Id == t2.Id)
        {
            return true;
        }
        else
        {
            return false;
        }
    }  

    public int GetHashCode(Type t)
    {
        return t.Id.GetHashCode();      
    }
}

You should pick his answer as being the correct one (although the next one is correct too)

like image 31
Adrian Iftode Avatar answered Oct 14 '22 11:10

Adrian Iftode