2013-04-04 14 views
7

Ho due liste:Differenza tra due liste preservare duplicati

var list1 = new List<string> { "A", "A", "B", C" }; 
var list2 = new List<string> { "A", "B" }; 

e desidero produrre un elenco come

var result = new[] { "A", "C" }; 

Se l'elenco è tutti gli elementi da list1 rimossi da list2 I non pensare che ci sia un metodo di estensione Linq per questo dal Except rimuove i duplicati.

Il modo non LINQ per fare questo sarebbe:

var tempList = list1.ToList(); 
foreach(var item in list2) 
{ 
    tempList.Remove(item); 
} 

ma mi chiedo se c'è un metodo di estensione LINQ che avrei potuto perdere.

Edit:

Poiché ci probabilmente non sono affatto ecco un metodo di estensione che ho fatto.

public static class LinqExtensions 
{ 
    public static IEnumerable<T> RemoveRange<T>(this IEnumerable<T> source, IEnumerable<T> second) 
    { 
     var tempList = source.ToList(); 

     foreach(var item in second) 
     { 
      tempList.Remove(item); 
     } 

     return tempList; 
    } 

    public static IEnumerable<TFirst> RemoveMany<TFirst, TSecond>(this IEnumerable<TFirst> source, IEnumerable<TSecond> second, Func<TSecond, IEnumerable<TFirst>> selector) 
    { 
     var tempList = source.ToList(); 

     foreach(var item in second.SelectMany(selector)) 
     { 
      tempList.Remove(item); 
     } 

     return tempList; 
    } 
} 

Usage:

list1.RemoveRange(list2) 

risposta

2

Guardando il tuo esempio, penso che vuoi dire "tutti gli elementi da lista2 rimossi da List1":

var lookup2 = list2.ToLookup(str => str); 

var result = from str in list1 
      group str by str into strGroup 
      let missingCount 
        = Math.Max(0, strGroup.Count() - lookup2[strGroup.Key].Count()) 
      from missingStr in strGroup.Take(missingCount) 
      select missingStr; 
+0

Gli elenchi sono di dimensioni arbitrarie, quindi uno potrebbe essere più grande. – Romoku

+0

Non ci sono ipotesi fatte qui sulle dimensioni relative degli elenchi. – Ani

+0

Oh, suppongo che ho scambiato il significato di "tutti gli elementi di list2 rimossi dalla lista1" – Romoku

1

Non LINQ, ma una sola riga in ogni caso:

list2.ForEach(l => list1.Remove(l)); 

btw ... sarebbe bello se List<int> avuto qualcosa di simile, ma per rimuovere AddRange un mucchio di oggetti allo stesso tempo.

1

Se non vi interessa circa l'ordine in cui gli elementi del risultato vengono, è possibile farlo con LINQ di GroupBy:

var a = new List<string>{"A","A", "B", "C"}; 
var b = new List<string>{"A", "B"}; 
var res = a.Select(e => new {Key=e, Val=1}) 
    .Concat(b.Select(e => new {Key=e, Val=-1})) 
    .GroupBy(e => e.Key, e => e.Val) 
    .SelectMany(g => Enumerable.Repeat(g.Key, Math.Max(0, g.Sum()))) 
    .ToList(); 

Ecco un demo on ideone.

Devo ammettere che la tua soluzione è molto più semplice della mia, quindi dovrebbe essere considerata come una semplice curiosità, un modo per dimostrare che questo potrebbe essere fatto anche con LINQ.

Ecco come funziona: per ogni elemento del primo elenco aggiungiamo una coppia chiave-valore con uno 1; per ogni elemento della seconda lista aggiungiamo una coppia chiave-valore con -1. Quindi raggruppiamo tutti gli elementi con la loro chiave, sommando i loro e quelli negativi, e produciamo tante chiavi quante sono il totale, assicurandoci di non selezionare nulla quando il risultato è negativo.

+0

bene funziona, ma questo è solo il modo più complicato. – Romoku

+0

@dasblinkenlight si trasforma in un'estensione per lui per semplificarlo. –

+2

Suppongo che a volte Linq non sia la risposta. – Romoku