2010-10-01 20 views
8

tengo una lista:Contando números duplicados en una lista

int list = { 1,1,2,3,4,4,5,7,7,7,10}; 

ahora tengo que hacer un programa que calcula los números dobles. Un número es doble cuando el número anterior es el mismo. Espero que entiendas. Entonces 1 es doble, 4 es dobles y tenemos 2 dobles en 7,7,7.

+1

¿Puede proporcionar un poco más de información sobre por qué/cómo? Se siente como una pregunta de tarea/entrevista. Sería muy simple de resolver simplemente usando un bucle y comparar previo al actual y almacenar/restablecer si se encuentra la coincidencia - solo un poco de lógica extra para detener el conteo de 7 veces. Si quisieras que se resolviera en linq o algo así, eso es más interesante. –

Respuesta

26

he aquí una solución en LINQ:

var doubles = list.Skip(1) 
        .Where((number, index) => list[index] == number); 

Esto crea otra secuencia omitiendo el primer miembro de la lista, y luego encuentra los elementos de ambas secuencias que tienen el mismo índice y el mismo valor. Se ejecutará en tiempo lineal, pero solo porque una lista ofrece acceso a O(1) por índice.

+2

A definitivamente +1. La respuesta es concisa, correcta (no probada, pero me arriesgo), muy inteligente y correctamente argumentada por qué se ejecuta en tiempo lineal. – Fede

+2

+1: ¡Eso es muy elegante! – RedFilter

+9

Imagínese copiar eso como una respuesta de tarea, luego tener que explicarlo a la clase (y al profesor) ... mwahahaha –

2

algo como esto puede funcionar:

list.GroupBy (l => l).Where (l => l.Count() > 1).SelectMany (l => l).Distinct(); 

EDIT:

el código anterior no consigue el resultado del PO quería. Aquí es una versión editada que está teniendo la inspiración de solución elegante de Ani a continuación: :)

list.GroupBy(l => l).Select(g=>g.Skip(1)).SelectMany (l => l); 
7

aquí es un enfoque que es relativamente simple, sólo repite una vez más de la secuencia, y funciona con cualquier secuencia (no sólo las listas):

public IEnumerable<T> FindConsecutiveDuplicates<T>(this IEnumerable<T> source) 
{ 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     T current = iterator.Current; 
     while (iterator.MoveNext()) 
     { 
      if (EqualityComparer<T>.Default.Equals(current, iterator.Current)) 
      { 
       yield return current; 
      } 
      current = iterator.Current; 
     } 
    } 
} 

Aquí hay otro que es aún más simple, ya que sólo una consulta LINQ, pero utiliza los efectos secundarios en la cláusula WHERE, que es desagradable:

IEnumerable<int> sequence = ...; 

bool first = true; 
int current = 0; 
var result = sequence.Where(x => { 
    bool result = !first && x == current; 
    current = x; 
    first = false; 
    return result; 
}); 

Una tercera alternativa, que es un poco más limpia, pero utiliza un método SelectConsecutive que es básicamente SelectPairs de this answer, pero cambió el nombre a ser un poco más claro :)

IEnumerable<int> sequence = ...; 
IEnumerable<int> result = sequence.SelectConsecutive((x, y) => new { x, y }) 
            .Where(z => z.x == z.y); 
+2

¿Qué quiere decir con "usa un efecto secundario"? –

+2

Mis ojos están sangrando. –

+0

@Lasse: Buen punto. Um, cambié mi plan. Espera, y voy a poner la versión de efecto secundario de nuevo :) –

6

Todo el mundo parece estar tratando de encontrar buenas maneras de hacerlo, por lo aquí hay una muy mala manera:

List<int> doubles = new List<int>(); 
Dictionary<int, bool> seenBefore = new Dictionary<int, bool>(); 

foreach(int i in list) 
{ 
    try 
    { 
     seenBefore.Add(i, true); 
    } 
    catch (ArgumentException) 
    { 
     doubles.Add(i); 
    } 
} 

return doubles; 

Por favor, no lo hagas así.

+0

jaja, +1 para el sentido del humor. Es viernes después de todo –

+0

Gracias. No estaba seguro de si obtendría un voto negativo: votos por publicar una respuesta incorrecta o votos positivos por decir que era malo. :-) – teedyay

+0

+1 No es una respuesta completamente diferente de Linq aparte de la excepción: puede usar ContainsKey o TryGetValue para evitar la excepción y estaría bien. –

0

Aquí vas con la respuesta en C# :)

int[] intarray = new int[] { 1, 1, 2, 3, 4, 4, 5, 7, 7, 7, 10 }; 

int previousnumber = -1; 
List<int> doubleDigits = new List<int>(); 
for (int i = 0; i < intarray.Length; i++) 
{ 
    if (previousnumber == -1) { previousnumber = intarray[i]; continue; } 
    if (intarray[i] == previousnumber) 
    { 
     if (!doubleDigits.Contains(intarray[i])) 
     { 
      doubleDigits.Add(intarray[i]); 
      //Console.WriteLine("Duplicate int found - " + intarray[i]); 
      continue; 
     } 
    } 
    else 
    { 
     previousnumber = intarray[i]; 
    } 
} 
0

Un ejemplo que (probablemente) se comporta mejor que el uso de LINQ, aunque es sin duda menos elegante:

for (int i = 1; i < list.Count; i++) 
    if (list[i] == list[i - 1]) 
     doubles.Add(list[i]); 
0

usted puede hacer esto :

list.GroupBy(i => i).Where(g => g.Count() > 1).SelectMany(g => g.Skip(1)) 

Esto es un poco como respuesta de @ KJN, excepto que creo que expresa los "dobles" y "dos dobles" cláusula en la pregunta un poco mejor:

  1. grupo de todos los enteros juntos
  2. sólo está interesado en los que aparecen más de una vez (g.Count() > 1)
  3. seleccione una lista lineal de los "dobles", siendo los que después de la primera (g.Skip(1))

PS: Estamos asumiendo aquí, eso no significa GroupBy primero ordenar la lista y si lo hace, que ese tipo no se ve influenciada negativamente por una lista de preclasificados ...

Cuestiones relacionadas