Daniel
Daniel

Reputation: 11064

Linq - Excluding items from different list types

Is there any way to select items in a list that aren't contained in another? For example:

list1 = From t In list1 Where Not list2.Contains(t.column1)

That gives me the error:

Value of type 'Integer' cannot be converted to '<anonymous type>'

which makes sense, since list2.Contains is expecting the same type as list2. However, the list types are different. I want only to select based on column comparisons.

Upvotes: 4

Views: 5854

Answers (4)

I created this extension method: Notice that the second sequence can have different type, however you have to specify the key and optionally the comparer.

  public static IEnumerable<TOuter> Except<TOuter, TInner, TKey>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
        Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, IEqualityComparer<TKey> comparer) {
     IEnumerable<TOuter> iguales = outer.Join(
        inner           : inner,
        outerKeySelector: tOuter => outerKeySelector(tOuter),
        innerKeySelector: tInner => innerKeySelector(tInner),
        resultSelector  : (o, _) => o,
        comparer        : comparer);
     return outer.Except(iguales);
  }

Here's how you can use it

     String name = "John";
     Char[] chars = { 'h', 'n' };
     var result = name.Except(
        inner           : chars,
        outerKeySelector: c => c,
        innerKeySelector: c => c); // result will contain J, o

Upvotes: 0

Ryan Versaw
Ryan Versaw

Reputation: 6495

Have you tried something like this?

list1 = From t In list1 Where Not list2.Any(l => t.column1 = l.column1 AndAlso t.column2 = l.column2)

I am unsure how efficient it would be, but I think it should work for you.

Upvotes: 6

Joel Coehoorn
Joel Coehoorn

Reputation: 416049

Look at the .Except() extension method, combined with a projection:

list1 = list1.Except(list2.Select(Function(l) l.ID))

Upvotes: 7

Jon Skeet
Jon Skeet

Reputation: 1502296

Well what does list2 actually contain? If you can express your query precisely, we can probably express it in LINQ. Without knowing what list1, list2 and column1 are it's hard to help.

What I would say is that List<T>.Contains is going to be O(n) for each item you check. If list2 is potentially non-small, you may well want to create a HashSet<T> - then each Contains call will be a lot faster.

But then again, when we know more about the situation we may well suggest a completely different solution. Please make the question as concrete as possible to get the best answer.

EDIT: If tvanfosson's solution works for you and if you're using LINQ to Objects, you've got a potential performance pit there. It would be better (IMO) to do the projection on list2 once and build a set:

Dim invalid = New HashSet(Of Integer)(list2.Select(Function(x) x.Id))
list1 = From t in list1 Where Not invalid.Contains(t.column1)

Upvotes: 7

Related Questions