2010-11-24 5 views
21

¿Es posible para mí usar LINQ de una manera que me permita determinar que "9" es el primer valor perdido en la lista ordenada sin utilizar un for-loop y comparando cada valor con el que está junto a él?¿Se puede usar LINQ para encontrar huecos en una lista ordenada?

var listStringVals = new [] { "7", "13", "8", "12", "10", "11", "14" }; 
// sort list to "7","8","10","11","12","13","14" 
var sortedList = listStringVals.OrderBy(c => int.Parse(c)).ToList(); 
// need some magic here to get the first gap in the sorted list 
+1

Simplemente curioso; ¿Por qué son 'string' y no como' int' o algo así? – BeemerGuy

+3

Un ciclo terminará siendo la forma más fácil y clara de escribir esto en C#. – mquander

+1

@BeemerGuy - Son cadenas porque representan parte de una cadena de ID almacenada en una base de datos. Simplemente estoy jugando con LINQ, tratando de tener una mejor idea de cuándo puede simplificar ciertas tareas. – Nate

Respuesta

52

Deje

var strings = new string[] { "7", "13", "8", "12", "10", "11", "14" }; 

Entonces

var list = Array.ConvertAll(strings, s => Int32.Parse(s)).OrderBy(i => i); 
// or 
var list = strings.Select(s => int.Parse(s)).OrderBy(i => i); 
// or 
var list = strings.OrderBy(s => int.Parse(s)); 

(nota this pregunta)

y luego

var result = Enumerable.Range(list.Min(), list.Count).Except(list).First(); // 9 
// or 
int min = list.Min(), max = list.Max(); 
var result = Enumerable.Range(min, max - min + 1).Except(list).First(); 
+0

Oh maaan ... tu código se ve más limpio =) +1 – BeemerGuy

+0

en realidad, en lugar de 'list.Count' en caso de que se compare con el último elemento, ¿cuál es el valor más alto? Si solo hay 5 elementos, pero el último elemento es 1000, esto no devolverá todos los huecos, solo los huecos hasta el número 5 – hunter

+0

+1 de genialidad! – Vishal

9

Aquí está una manera para que pueda empezar (utilicé int valores aquí):

List<int> listStringVals = (new int[] { 7, 13, 8, 12, 10, 11, 14 }).ToList(); 
List<int> SortedList = listStringVals.OrderBy(c => c).ToList(); 
List<int> Gaps = Enumerable.Range(SortedList.First(), 
            SortedList.Last() - SortedList.First() + 1) 
          .Except(SortedList).ToList(); 
+0

Excelente, gracias por ilustrar una buena manera de obtener todos los huecos. Esta publicación ha generado algunas respuestas impresionantes, es una pena que solo pueda etiquetar una como respuesta. ¡Gracias a todos! – Nate

+0

+1 por molestarse en escribir el cálculo correcto de 'Rango' al comienzo! – jball

4
var listStringVals = new string[] {"7", "13", "8", "12", "10", "11", "14"}; 
var sortedInts = listStringVals.Select(c => int.Parse(c)).OrderBy(x => x); 
var noGaps = Enumerable.Range(sortedInts.First(), 
           sortedInts.Last() - sortedInts.First() + 1); 
var missing = noGaps.Except(sortedInts).Select(x => x.ToString()).First(); 

Edit: fija generación gama gracias a BeemerGuy's answer. Aún dejando la mía, ya que no pasa por alto la fealdad de una lista de string representaciones de int s :)

+0

+1 esta es la manera completa correcta de hacerlo – hunter

3

(abatishchev me ganó de mano, pero su respuesta es mejor de todos modos. Sin embargo, ya que las soluciones alternativas a la misma problema puede ser divertido, todavía estoy publicando esto.)

Hackity hack hack. Pero trabajando, si realmente quiere hacer esto. El rendimiento será horrible, porque esta técnica no se detendrá cuando encuentre la respuesta: ¡siempre pasará por encima de cada número! Pero funcionará:

public static int FindFirstMissing(IEnumerable<int> sequence) 
{ 
    bool found = false; 

    int agg = sequence.Aggregate((aggregate, next) => { 
     if (found) 
      return aggregate; 

     if (next - aggregate != 1) { 
      found = true; 
      return aggregate + 1; 
     } 

     return next; 
    }); 

    if (!found) 
     throw new ArgumentException("sequence", "Contains no missing numbers."); 

    return agg; 
} 
+0

¡Realmente genial! 'Aggregate()' es la herramienta más poderosa del conjunto, creo. Pero un poco complejo al mismo tiempo. – abatishchev

+0

Y no es eficiente para casos como este. ;) – cdhowie

+0

+1 para un nuevo enfoque, no he revisado las respuestas a esta pregunta en mucho tiempo, pero esta es interesante. Gracias a todos los que se tomaron el tiempo para publicar, agradezco el tiempo y la perspectiva. – Nate

3
string firstGap = sortedList 
    .Zip(sortedList.Skip(1), (f, s) => Tuple.Create(f, s)) 
    .First(tup => (int.Parse(tup.Item1) + 1) != int.Parse(tup.Item2)).Item1; 

deben darle el primer punto antes de la primera brecha, por lo que el primer elemento que falta es:

string gap = (int.Parse(firstGap) + 1).ToString(); 
+0

Manera muy creativa de resolver el problema. ¡Realmente he disfrutado leyendo todas las respuestas! – Nate

+1

@jball - Crea una secuencia de pares entre un elemento de la lista y el siguiente elemento, p. ("7", "8"), ("8", "10) ... (" 11 "," 14 "). La tercera línea busca la primera tupla donde el primer elemento no es menos que el segundo . – Lee

+0

gracias por la explicación (lo siento por eliminar mi comentario) - He estado jugando con él durante los últimos 20 minutos y realmente había comido lo que hizo cuando noté su comentario. – jball

1

que es un poco tarde, pero creo que es un lugar fresco forma de hacer esto:

List<int> listStringVals = (new int[] { 7, 13, 8, 12, 10, 11, 14 }).ToList(); 
      listStringVals.Sort(); 
      return listStringVals.Skip(1).Select((x, i) => x - listStringVals[i] == 1).Any(x => !x); 
+1

Me encanta ver diferentes enfoques para resolver el mismo problema. Gracias por publicar, incluso si es un poco tarde. Tomarse el tiempo para asimilar todos los diferentes las respuestas ya han dado sus frutos. Gracias de nuevo a todos los que se tomaron su tiempo para publicar en este hilo. – Nate

0

Por qué no utilizar All ya que todos los miembros de la colección tienen que ajustarse a la crite ría ...

Ejemplo

someVar.All(v => someVar.Contains(v + 1) || v == someVar.Last())

entonces usted no tiene que pedir y su más agradable.

Podrías ordenar después de este paso o incluso en el caso de que lo necesites pero yo personalmente solo usaría la colección ordenada y haré que trabaje para mí.

Agarraría los valores si lo necesitara después de realizar la comprobación, luego devolverá el resultado del cheque u ocultará si lo desea por algún motivo mediante una modificación de varias líneas anterior junto con una lista para almacenar los valores en

Por ej.

someVar.All((v) => { 
    bool result = someVar.Contains(v + 1) || v == someVar.Last(); 
    if(!result) someList.Add(v); 
    return true; 
}); 

Comprobación del recuento de la lista (que podría ser pedido) para un valor distinto de cero para indicar si se cumplen o no.

Cuestiones relacionadas