Hai scritto:
modo semplice giro che sto usando è Where(v => !secondList.Contains(v))
Quando si esegue questa operazione, c'è ancora Distict fatto con secondList
.
Ad esempio:
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
ho creato un metodo di estensione non avere distinto affatto. Examle di utilizzo:
var result2Strings = firstStrings.ExceptAll(secondStrings).ToList(); // null, 3, 3
Questo è ciò che fa:
Questa è la fonte:
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;
}
}
}
Edit: A Implementazione più veloce, basato sul commento di 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 sul mio blog (anche la variante per Intersect e Union)
Beh, questo ha un po 'più senso. È passato molto tempo da quando ho preso la matematica. Grazie. – Stephan
@Stephan - Anche se, devo dire, il comportamento documentato mi sembra un po 'strano dato che 'IEnumerable' è una sequenza e non un set, quindi avere un metodo di estensione generico per una sequenza ha impostato la semantica è un po 'strano Ma di nuovo, suppongo che l'alternativa di avere un metodo 'Except' da' IEnumerable 'abbia semantica di sequenza e uno da' ISet 'la semantica impostata sia ancora peggiore, poiché' ISet 'eredita' IEnumerable 'e quindi la semantica sarebbe diversa a seconda di come il metodo di estensione era vincolato dal compilatore. –
@Greg, Mono sembra ottenere questo [sbagliato] (http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/System.Core/System.Linq/Enumerable.cs#l794) (questo è in realtà il secondo bug Mono Enumerable che ho trovato attraverso StackOverflow). Restituisce gli elementi dal primo tutte le volte che appaiono. La mia domanda è, se è una differenza di set, perché la documentazione mostra Enumerable.Except preservando l'ordine dei primi elementi (dice anche che restituisce "Una sequenza")? Gli insiemi non conservano l'ordine. Enumerable.Except garantito a? –