1

I have two generic lists where I want to run a couple of Linq queries to find out:

  1. Are any of lists A items found in list B

  2. Are all of lists A items found in list B

Here are the lists:

var ListA = new List<long>()
var ListB = new List<MyObject>()

MyObject is defined as:

public class MyObject
  {
    public long ItemId { get; set; }    
    // ... Other stuff...
  }

I am trying to determine two things (two queries): 1. Do any of the longs in ListA match any of the MyObject.ItemId in ListB? And 2. Can all of the longs in ListA be found in ListB?

ListA and ListB can be different lengths. For number 2, I would need all of ListA's items found in ListB, but not vice-versa. I hope this makes sense.

Thanks,

-Scott

Scott
  • 874
  • 3
  • 12
  • 36
  • I'm a little confused about part 2. Do you want to know if all the items in ListA are contained in the ItemId's of B or do you want a List of ListA values contained in the ItemId's of B. Slightly different queries. Your last sentence it what has me confused. – Craig Suchanec Apr 13 '11 at 22:54
  • I want each long in ListA to equal at least one MyObject.ItemId in ListB. – Scott Apr 13 '11 at 23:59

4 Answers4

6

First, you only care about the ItemIds in ListB, so:

var bIDs = ListB.Select(x => x.ItemId);

To answer the first part of your question, I would approach this by finding the intersection of the two lists (the set of all items they share). If it has at least one element in it, then there is overlap between the two.

var sharedIds = ListA.Intersect(bIDs);
if (sharedIds.Any())
    // list A contains at least one ItemID which ListB contains

As for the second part, you want to see if list A is a subset of list B. Searching for this, Stack Overflow presents a clean solution:

if (!ListA.Except(bIDs).Any())
    // yes, list A is a subset of list B

This snippet works because ListA.Except(bIDs) finds the elements that ListA has that bIDs doesn't. If this is empty, then ListA doesn't contain anything that bIDs doesn't. Thus, everything that is in ListA is also in bIDs.

Here's an example: A = {1, 2}; B = {1, 2, 3}. A is a subset of B. A.Except(B) gives you an empty set - B has both 1 and 2, so can't be in the resulting list, and there isn't anything left in B. So when A is a subset of B, A.Except(B).Any() gives false, as there are no elements in the result; so we obviously negate it if we want to handle that case.

For completeness, if we swap A and B round such that A is not a subset of B: A = {1, 2, 3}; B = {1, 2}, then A.Except(B) gives {3}. It can't contain 1 or 2, because B contains 1 and 2. But B doesn't contain 3, so A.Except(B) can contain it. As {3} contains one element, it isn't empty, so A.Except(B).Any() is true. Negated, it is false if A is not a subset of B.

My explanation is a little terse; if you want to look things up further (and I recommend you do - a little set theory can go a long way), A.Except(B) is LINQ's name for the set difference, or relative set complement. Wikibooks has a decent introduction to set theory if you are so inclined.

Community
  • 1
  • 1
Lucas Jones
  • 19,767
  • 8
  • 75
  • 88
  • @flesh: With the `if`s, etc. it's a little verbose; is there something in particular you think could be simplified? – Lucas Jones Apr 13 '11 at 23:23
  • Correct me if I'm wrong, but I think the NOT operator needs to be removed in order for the your last code example to work correct. I think this is exactly what I am looking for. Thanks! – Scott Apr 13 '11 at 23:39
  • @Scott: I've had a little go at an explanation there; I can't test any C# right now, but I think it's correct. If not, feel free to put me in the stocks and throw virtual vegetables at me. ;) – Lucas Jones Apr 14 '11 at 00:02
  • Complicated? It looks as simple as it can get! Great answer! – Jonas Jun 11 '14 at 11:21
1
var value1 =
(
    from itemA in ListA
    where ListB.Any(itemB => itemB.ItemID == itemA)
    select item
).Count();

var value2 = value1 == ListA.Count();
Akram Shahda
  • 14,655
  • 4
  • 45
  • 65
0

To just test the conditions, assuming you extract a list of ItemIds into listB:

bool inListA = listA.Any(x => listB.Contains(x));

bool allInListB = listA.All(x => listB.Contains(x));

To test in place without extracting a separate list if ItemIds

bool inListA = listA.Any(x => listB.Select(b => b.ItemId).Contains(x));

bool allInListB = listA.All(x => listB.Select(b => b.ItemId).Contains(x));
flesh
  • 23,725
  • 24
  • 80
  • 97
0

If you need to answer all three questions at the same time then it's likely that a pure LINQ solution won't be optimal, since the individual queries will each need to perform the same intersection operation. Do the intersection once, and then use that result to answer your three questions:

var tempSet = new HashSet<long>(ListA);
int uniqueAItemCount = tempSet.Count;

// 2b. "I would need all of ListA's items found in ListB, but not vice-versa."
tempSet.IntersectWith(ListB.Select(x => x.ItemId));
// tempSet now contains all items from ListA also found in ListB
// we can use this result to answer the other two questions...

// 1. "Do any of the longs in ListA match any of the MyObject.ItemId in ListB?"
bool anyAFoundInB = tempSet.Count > 0;

// 2a. "Can all of the longs in ListA be found in ListB?"
bool allAFoundInB = tempSet.Count == uniqueAItemCount;
LukeH
  • 263,068
  • 57
  • 365
  • 409