2011-02-16 8 views
9

Estoy trabajando en una asignación de Factor común más grande y Mínimo común múltiple y tengo que enumerar los factores comunes. Intersección() no funcionará porque elimina duplicados. Contiene() no funcionará porque si ve el int en la segunda lista, devuelve todas las entradas coincidentes de la primera lista. ¿Hay alguna manera de hacer una Intersección que no sea distinta?¿Cómo hago una intersección de lista de enteros manteniendo los duplicados?

Edit: Lo siento por no proporcionar un ejemplo, esto es lo que quiere decir:

si tengo los conjuntos:

{1, 2, 2, 2, 3, 3, 4, 5} 
{1, 1, 2, 2, 3, 3, 3, 4, 4} 

me gustaría que la salida

{1, 2, 2, 3, 3, 4} 
+3

Si a es {3,3,3,3} yb es {3,3}, ¿cuántos 3 espera en la salida? 2, 4 o 6? – Ani

+0

Creo que las respuestas a continuación confunden la pregunta. La pregunta correcta es 'Encontrar la intersección de dos conjuntos'. El problema es que el operador 'Intersecar' elimina duplicados: resuelve el problema sin eliminar los duplicados. –

+1

dos 3s en la salida – DuckReconMajor

Respuesta

7
ILookup<int, int> lookup1 = list1.ToLookup(i => i); 
ILookup<int, int> lookup2 = list2.ToLookup(i => i); 

int[] result = 
(
    from group1 in lookup1 
    let group2 = lookup2[group1.Key] 
    where group2.Any() 
    let smallerGroup = group1.Count() < group2.Count() ? group1 : group2 
    from i in smallerGroup 
    select i 
).ToArray(); 

La expresión where es técnicamente opcional, creo que hace que la intención sea más clara.


Si desea un código más conciso:

ILookup<int, int> lookup2 = list2.ToLookup(i => i); 

int[] result = 
(
    from group1 in list1.GroupBy(i => i) 
    let group2 = lookup2[group1.Key] 
    from i in (group1.Count() < group2.Count() ? group1 : group2) 
    select i 
).ToArray(); 
+0

Esto funcionó para mí. ¡Gracias! – DuckReconMajor

+0

+1 Me recuerda mucho a esta pregunta, que ambos respondimos. :) http://stackoverflow.com/questions/4460940/mastermind-scoring-algorithm-in-c-using-linq – Ani

+0

@DuckReconMajor, mi respuesta evita contar por completo los dos grupos coincidentes, esto tiene que ser mejor, sin embargo, es tres años tarde :-) http://stackoverflow.com/a/21776543/659190 – Jodrell

1

¿Está buscando algo como esto? Que debe ser bastante-mucho O (n + m), en donde n es el número de elementos en first y m es el número de elementos de second.

public static IEnumerable<T> Overlap<T>(this IEnumerable<T> first, 
    IEnumerable<T> second, IEqualityComparer<T> comparer = null) 
{ 
    // argument checking, optimisations etc removed for brevity 

    var dict = new Dictionary<T, int>(comparer); 

    foreach (T item in second) 
    { 
     int hits; 
     dict.TryGetValue(item, out hits); 
     dict[item] = hits + 1; 
    } 

    foreach (T item in first) 
    { 
     int hits; 
     dict.TryGetValue(item, out hits); 
     if (hits > 0) 
     { 
      yield return item; 
      dict[item] = hits - 1; 
     } 
    } 
} 
0
  • Encuentra la intersección de las dos listas.
  • Grupo de las listas de los artículos que se cruzan
  • incorporarse a los grupos y seleccione el Min (Count) para cada elemento
  • Acoplar en una nueva lista.

ver más abajo:

var intersect = list1.Intersect(list2).ToList(); 
var groups1 = list1.Where(e => intersect.Contains(e)).GroupBy(e => e); 
var groups2 = list2.Where(e => intersect.Contains(e)).GroupBy(e => e); 

var allGroups = groups1.Concat(groups2); 

return allGroups.GroupBy(e => e.Key) 
    .SelectMany(group => group 
     .First(g => g.Count() == group.Min(g1 => g1.Count()))) 
    .ToList(); 
0

Ésta es una forma de hacerlo. Para ser justos, es muy similar a la respuesta de David B, excepto que usa un join para hacer la asociación.

IEnumerable<Foo> seqA = ... 
IEnumerable<Foo> seqB = ... 

var result = from aGroup in seqA.GroupBy(x => x) 
      join bGroup in seqB.GroupBy(x => x) 
         on aGroup.Key equals bGroup.Key 
      let smallerGroup = aGroup.Count() < bGroup.Count() 
           ? aGroup : bGroup 
      from item in smallerGroup 
      select item; 
0

Usted podría utilizar esta extensión genérica que escribí para another answer, es esencialmente una sola declaración de LINQ. Tenga en cuenta que usa Zip para evitar la enumeración completa innecesaria de grupos coincidentes.

public static IEnumerable<T> Commom<T>(
     this IEnumerable<T> source, 
     IEnumerable<T> sequence, 
     IEqualityComparer<T> comparer = null) 
{ 
    if (sequence == null) 
    { 
     return Enumerable.Empty<T>(); 
    } 

    if (comparer == null) 
    { 
     comparer = EqualityComparer<T>.Default; 
    } 

    return source.GroupBy(t => t, comparer) 
     .Join(
      sequence.GroupBy(t => t, comparer), 
      g => g.Key, 
      g => g.Key, 
      (lg, rg) => lg.Zip(rg, (l, r) => l), 
      comparer) 
     .SelectMany(g => g); 
} 

esto permite,

new[] {1, 2, 2, 2, 3, 3, 4, 5}.Common(
    new[] {1, 1, 2, 2, 3, 3, 3, 4, 4}).ToArray() 

mantener el orden de la secuencia de origen, según se desee.

Cuestiones relacionadas