Se si utilizza questo:
var newData = destination.Except(data.Select(x => f(x)));
Dovete proiettare 'dati' per lo stesso tipo di contenuto in ' destinazione', ma usando il codice seguente si potrebbe sbarazzarsi di questa limitazione:
//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;
}
}
Alcuni potrebbero obiettare che è la memoria inefficiente a causa dell'uso di un HashSet. Ma in realtà il metodo Enumerable.Except del framework sta facendo lo stesso con una classe interna simile chiamata 'Set' (ho dato un'occhiata decompilando).
Povero vecchio ciclo "for', una volta era così utile ma, ahimè, non rende mai felici le persone con un solo liner. – Marc
@Marc: Non sono d'accordo con il sentimento che stai esprimendo. Abbiamo modi di scrivere il codice ora che esprime più chiaramente l'intento senza preoccuparsi del meccanismo. 'for' esprime meccanismi e oscura l'intento. Gli one-liner basati su LINQ che stai denigrando spesso (sì, non sempre) esprimono meglio l'intenzione e nascondono i meccanismi. Questo porta a un codice che è più facile da capire e mantenere. – jason
@Jason, mentre ero impertinente, tutte le funzioni che si proiettano in una proiezione come se fosse solo una supposizione di intenti. – Marc