If you use this :
var newData = destination.Except(data.Select(x => f(x)));
You have to project 'data' to same type contained in 'destination', but using the code below you could get rid of this limitation :
//Here is how you can compare two different sets.
class A { public string Bar { get; set; } }
class B { public string Foo { get; set; } }
IEnumerable<A> setOfA = new A[] { /*...*/ };
IEnumerable<B> setOfB = new B[] { /*...*/ };
var subSetOfA1 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo);
//alternatively you can do it with a custom EqualityComparer, if your not case sensitive for instance.
var subSetOfA2 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo, StringComparer.OrdinalIgnoreCase);
//Here is the extension class definition allowing you to use the code above
public static class IEnumerableExtension
{
public static IEnumerable<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect)
{
return Except(first, second, firstSelect, secondSelect, EqualityComparer<TCompared>.Default);
}
public static IEnumerable<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
return ExceptIterator<TFirst, TSecond, TCompared>(first, second, firstSelect, secondSelect, comparer);
}
private static IEnumerable<TFirst> ExceptIterator<TFirst, TSecond, TCompared>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
HashSet<TCompared> set = new HashSet<TCompared>(second.Select(secondSelect), comparer);
foreach (TFirst tSource1 in first)
if (set.Add(firstSelect(tSource1)))
yield return tSource1;
}
}
Some may argue that's memory inefficient due to the use of an HashSet. But actually the Enumerable.Except method of the framework is doing the same with a similar internal class called 'Set' (I took a look by decompiling).
for
loop, he was once so useful but, alas, he never makes the one-liner people happy. – Karolefor
expresses mechanisms and obscures intent. The LINQ-based one-liners that you are decrying often (yes, not always) better express intent and hide mechanisms. This leads to code that is easier to understand and maintain. – Ramsesvar newData = MeaningfulFunctionNameHere(destination, data);
In both cases, we make an assumption about what is really happening. In reality, I was really just poking fun at the LINQ is cool and applies everywhere crowd. – Karole