jazza1000
jazza1000

Reputation: 4247

How to compare properties of a subitem in 2 lists in linq

I have the following objects:

public interface ITray
{
int OrderNo {get; set;}
IEnumerable<ITrayItem> TrayItems {get;}
}

public interface ITrayItem
{
int Aisle {get; set;}
}

Now, I have two List objects,

List<ITray> selectedTrays
List<ITray> poolTrays

What I am trying to do is for each element in poolTrays, I want to compare the Aisles that are in the list of selected trays. If all of the Aisles match, I want to add it to a list of trays to return. I'm just tying myself in knots a bit trying to get the linq working with the querying of a property of a collection inside a list and returning the items in the list that match.

This is what I have at the moment:

List<int> selectedAisles = (from tray in selectedTrays
                            from item in tray.TrayItems
                            select item.Aisle).Distinct().ToList()

List<ITray> trayswithMatchingAisles =
           (from t in poolTrays
            from item in t.TrayItems
            where selectedAisles.Contains(item.Aisle)
            select t).ToList();

So, if I have selected Trays A, B, C with aisles in brackets A[1,2,3] B[4,5,6] c[7,8,9]

then a poolTray with TrayItems in aisles [7,9] should return successfully, but a pool tray with TrayItems [7,8,9,10] should not be returned in the list.

At the moment, I am passing in (just) [7,9] in my poolTray list, and 2 instances of it are returned in my Linq query

Upvotes: 3

Views: 1071

Answers (3)

King King
King King

Reputation: 63387

var result = poolTrays.Where(x => selectedTrays.Any(z=>z.TrayItems.Select(y => y.Aisle)
                                                            .Intersect(x.TrayItems.Select(k => k.Aisle))
                                                            .Count() == x.TrayItems.Count()));

Upvotes: 2

p.s.w.g
p.s.w.g

Reputation: 149108

Something like this should work:

List<int> selectedAisles = 
    (from tray in selectedTrays
     from item in tray.TrayItems
     select item.Aisle)
    .Distinct().ToList();

List<ITray> trayswithMatchingAisles =
    (from t in poolTrays
     where t.TrayItems.Select(i => i.Aisle)
            .All(a => selectedAisles.Contains(a))
     select t)
    .ToList();

But this can be simplified to:

List<ITray> trayswithMatchingAisles =
    (from t in poolTrays
     where t.TrayItems.Select(i => i.Aisle)
            .All(a => selectedTrays
                .SelectMany(s => s.TrayItems)
                .Select(i => i.Aisle)
                .Contains(a))
     select t)
    .ToList();

Or this:

List<ITray> trayswithMatchingAisles = poolTrays
    .Where(t => t.TrayItems
        .Select(i => i.Aisle)
        .All(a => selectedTrays
                .SelectMany(s => s.TrayItems)
                .Select(i => i.Aisle)
                .Contains(a)))
    .ToList();

Upvotes: 2

thepirat000
thepirat000

Reputation: 13124

I think you need to use the "SelectMany" extension, this is to flat queries that return lists of lists.

For example:

var distinctSelectedItems = selectedTrays.SelectMany(t => t.TrayItems).Select(ti => ti.Aisle).Distinct();
bool success = poolTrays.SelectMany(t => t.TrayItems).All(ti => distinctSelectedItems.Contains(ti.Aisle));

You can also create a HashSet, in order to have O(1) performance, instead of O(n) for the List.Contains.

var distinctSelectedItems = new HashSet<int>(selectedTrays.SelectMany(t => t.TrayItems).Select(ti => ti.Aisle));
bool success = poolTrays.SelectMany(t => t.TrayItems).All(ti => distinctSelectedItems.Contains(ti.Aisle));

Good luck.

Upvotes: 2

Related Questions