2010-09-21 26 views
7

Estoy buscando una manera de calcular el rango de un conjunto de dar número, por ejemplo.Obtener rango de enteros con linq

si tuviera H555, H567, H589, H590, H591, H592, H593, H594, H595, H596, H597

quisiera salida del H555, H567, H589-H597.

He revisado las preguntas relevantes y no encuentro nada parecido a lo que estoy buscando.

Gracias

+7

Do tiene que ser con LINQ? –

+0

¿Qué es ese conjunto de números? ¿Una cuerda? ¿Una matriz? –

+0

Estoy abierto a cualquier solución. es un conjunto de facturas para ser exactos como H272 para la factura de la casa 272, pero esencialmente un conjunto de cadenas – Jesse

Respuesta

4

Creo que LINQ es realmente sobrecarga aquí, pero si quieres que aquí están:

 int[] arr = { 555, 567, 589, 590, 591, 592, 593, 594, 595, 596, 597 }; 
     int gr = 0; 
     var q = arr 
      .Skip(1) 
      .Select((x, i) => new { x, group = (x - arr[i]) == 1 ? gr : gr++ }) 
      .GroupBy(a => a.group) 
      .Select(
       a => a.Count() == 1 
        ? a.First().x.ToString() 
        : string.Format("{0}-{1}", a.First().x, a.Last().x)); 
     foreach (var item in q) 
     { 
      Console.Write(item); 
      Console.Write(", "); 
     } 
+0

Creo que puedo hacer que esto funcione. Gracias – Jesse

+2

@Jesse, personalmente, prefiero la respuesta de Jon. Es mejor en términos de rendimiento tanto en la CPU como en la memoria. Solo quería mostrar cómo puedes hacerlo en Linq, eso no significa que debas hacerlo en Linq. – Andrey

+0

Acepto que la respuesta de Jon es más eficiente, pero en mi caso, esto es exactamente lo que estaba buscando. – Jesse

8

Bueno, me gustaría hacer algo como esto:

public sealed class Range 
{ 
    public int Low { get; private set; } 
    public int High { get; private set; } 

    public Range(int low, int high) 
    { 
     this.Low = low; 
     this.High = high; 
    } 
} 

Entonces (no está comprobado, puede incluso no compilar, pero espero que pueda obtener la deriva):

public static IEnumerable<Range> FindRanges(IEnumerable<int> values) 
{ 
    using (IEnumerator<int> iterator = values.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     int low = iterator.Current; 
     int high = low; 
     while (iterator.MoveNext()) 
     { 
      int next = iterator.Current; 
      if (next > high + 1) 
      { 
       // Previous range (or possibly single value) has finished 
       yield return new Range(low, high); 
       low = next; 
      } 
      high = next; 
     } 
     // Yield trailing range 
     yield return new Range(low, high); 
    } 
} 

No creo que esto sea particularmente fácil de hacer usando LINQ directo, para ser honesto.

EDIT: Para adaptar este ahora que todo empieza con H, sólo tiene que utilizar:

var numbers = strings.Select(x => int.Parse(x.Substring(1)); 
var ranges = FindRanges(numbers); 

var rangeStrings = ranges.Select(r => r.High == r.Low 
            ? "H" + r.Low : "H" + r.Low + "-" + r.High); 
var result = string.Join(",", rangeStrings); 
+0

Habría estado bien hasta que haya agregado "H" al comienzo de cada artículo. –

+0

¿Quizás use la H como clave en un diccionario int como el valor, calcule el rango y luego reensamble? – Jesse

+0

@Jesse: lo que describes crearía un diccionario donde cada tecla era la misma (es decir, no única). ¿Por qué no asumir H, poner los ints en una SortedList entonces? –

Cuestiones relacionadas