Usted escribió:
manera simple en torno estoy usando es Where(v => !secondList.Contains(v))
Al hacer esto, todavía hay Distict hecho con secondList
.
Por ejemplo:
var firstStrings = new [] { "1", null, null, null, "3", "3" };
var secondStrings = new [] { "1", "1", "1", null, null, "4" };
var resultStrings = firstStrings.Where(v => !secondStrings.Contains(v)); // 3, 3
creé un método de extensión no tener distinta en absoluto. Examle de uso:
var result2Strings = firstStrings.ExceptAll(secondStrings).ToList(); // null, 3, 3
Esto es lo que hace:
Ésta es la fuente:
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
// Do not call reuse the overload method because that is a slower imlementation
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var secondList = second.ToList();
return first.Where(s => !secondList.Remove(s));
}
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var comparerUsed = comparer ?? EqualityComparer<TSource>.Default;
var secondList = second.ToList();
foreach (var item in first)
{
if (secondList.Contains(item, comparerUsed))
{
secondList.Remove(item);
}
else
{
yield return item;
}
}
}
Editar: Una Implementación más rápida, basado en la observación de DigEmAll
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
return ExceptAll(first, second, null);
}
public static IEnumerable<TSource> ExceptAll<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var secondCounts = new Dictionary<TSource, int>(comparer ?? EqualityComparer<TSource>.Default);
int count;
int nullCount = 0;
// Count the values from second
foreach (var item in second)
{
if (item == null)
{
nullCount++;
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
secondCounts[item] = count + 1;
}
else
{
secondCounts.Add(item, 1);
}
}
}
// Yield the values from first
foreach (var item in first)
{
if (item == null)
{
nullCount--;
if (nullCount < 0)
{
yield return item;
}
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
if (count == 0)
{
secondCounts.Remove(item);
yield return item;
}
else
{
secondCounts[item] = count - 1;
}
}
else
{
yield return item;
}
}
}
}
More info en mi blog (también variante para Intersecar y Unión)
Bueno, eso tiene un poco más de sentido. Ha sido mucho tiempo desde que tomé las matemáticas establecidas. Gracias. – Stephan
@Stephan - Aunque, tengo que decir que el comportamiento documentado me parece un poco extraño dado que 'IEnumerable' es una secuencia y no un conjunto, por lo que tener un método de extensión de propósito general para una secuencia ha establecido una semántica es algo extraño Pero, de nuevo, supongo que la alternativa de tener un método 'Except' de' IEnumerable 'tiene una semántica de secuencia y una de' ISet 'ha establecido que la semántica es aún peor, ya que' ISet 'hereda' IEnumerable 'y así la semántica diferiría dependiendo de cómo el método de extensión estaba obligado por el compilador. –
@Greg, Mono parece obtener esto [mal] (http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/System.Core/System.Linq/Enumerable.cs#l794) (esto es en realidad el segundo error de Mono Enumerable que he encontrado a través de StackOverflow). Devuelve elementos del primero tantas veces como aparezcan. Mi pregunta es, si se trata de una diferencia establecida, ¿por qué la documentación muestra Enumerable. Excepto preservar el orden de los primeros elementos (también dice que devuelve "A sequence")? Los conjuntos no conservan el orden. ¿Es Enumerable.Except garantizado? –