Linq dilemma

I am having trouble efficiently selecting the information that I need to display. Hope someone else understands better how to solve this.

Considering the following data structures,

public class Department
{
   public int ID { get; set; }
   public string Name { get; set; }
   public IList<Product> Products{ get; set; }
}

public class Product
{
   public int ID { get; set; }
   public string Name { get; set; }
}

      

And given the following data

Department1 = 
{
    Id=1,
    Name="D1",
    Products = {new Product{Id=1, Name="Item1"}, new Product{Id=2, Name="Item2"}
}

Department2 = 
{
    Id=2,
    Name="D2",
    Products = {new Product{Id=2, Name="Item2"}, new Product{Id=3, Name="Item3"}
}

      

How to choose that "Item2" is common to "D1" and "D2"?

I tried using an intersection query, but it seems to require that the two deferred query plans intersect, not two IEnumerables or ILists.

Any help with this is greatly appreciated.

Edit: Looks like I wasn't very precise trying to keep things simple.

I have a list of departments, each of which contains a list of products. Given these listings, how to choose a different list of products based on certain criteria. My criteria in this case is that I want to select only those products that exist in all my departments. I only want data that traverses all the elements.

+1


a source to share


7 replies


My Intersect works fine:

        Department department1 = new Department
        {
            Id = 1,
            Name = "D1",
            Products = new List<Product> () { new Product { Id = 1, Name = "Item1" }, new Product { Id = 2, Name = "Item2" } }
        };

        Department department2 = new Department
        {
            Id = 2,
            Name = "D2",
            Products = new List<Product>() { new Product { Id = 2, Name = "Item2" }, new Product { Id = 3, Name = "Item3" } }
        };

        IEnumerable<Product> products = department1.Products.Intersect(department2.Products, new ProductComparer());

        foreach (var p in products)
        {
            Console.WriteLine(p.Name);
        }

      



(edit)

    public class ProductComparer : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {
            return x.Name == y.Name && x.Id == y.Id;
        }

        public int GetHashCode(Product obj)
        {
            return obj.Id.GetHashCode() ^ obj.Name.GetHashCode();
        }
    }

      

+1


a source


If you want to use Linq, you can flatten the collection of products and link them to the parent of the object (so that you have a collection of (Product, Department) pairs) and then regroup to Product.

var sharedItems = new[] { department1, department2 }
                .SelectMany(d => d.Products, (dep, prod) => new { Department = dep, Product = prod })
                .GroupBy(v => v.Product)
                .Where(group => group.Count() > 1);

      



The result of this query is an IGroupings enumeration, where the key is a product and contains sections with that product.

+1


a source


// Get a list of all departments.
IEnumerable<Department> departments = GetAllDepartments();

// Get a list of all products.
var products = departments.SelectMany(d => d.Products).Distinct();

// Filter out all products that are not contained in all departments.
var filteredProducts = products.
    Where(p => departments.All(d => d.Products.Contains(p)));

      

If you combine both queries, you get the following.

var filteredProducts = departments.
    SelectMany(d => d.Products).
    Distinct().
    Where(p => departments.All(d => d.Products.Contains(p)));

      

+1


a source


I may not fully understand the question, but would this work?

var commonProducts = new List<Product>();

Department1.Products.ForEach(delegate(Product product)
{
    if (Department2.Products.Contains(product))
    {
        commonProducts.Add(product);
    }
});

      

0


a source


Use SelectMany to create a flattened list of product / department pairs (perhaps in an anonymous class), then use GroupBy to group by product name (and, for example, handle multiple instances of the same product in the same department).

Will expand if I get a chance later.

0


a source


Project each department into IEnumerable<Product>

. Aggregate Product Lists — Use the first as a starting point and Intersect on the rest of the lists.

List<Product> commonProducts = departments
  .Select(d => d.Products.AsEnumerable() )
  .Aggregate( (soFar, nextList) => soFar
    .Intersect(nextList, productComparer) )
  .ToList()

      

You would need to implement ProductComparer to get around referential equality - if Product was a struct, you wouldn't need to do this.

0


a source


LINQ query syntax is better suited for these types of queries:

var q = from p1 in Department1.Products
        join p2 in Department2.Products
        on p1.Id equals p2.Id
        select p1;

foreach (Product p in q) Console.WriteLine(p.Name);

      

-1


a source







All Articles