2011-01-13 16 views

Respuesta

36
var array = new int[] { 1, 2, 3, 4, 7, 8, 11, 15, 16, 17, 18 }; 

var result = string.Join(",", array 
    .Distinct() 
    .OrderBy(x => x) 
    .GroupAdjacentBy((x, y) => x + 1 == y) 
    .Select(g => new int[] { g.First(), g.Last() }.Distinct()) 
    .Select(g => string.Join("-", g))); 

con

public static class LinqExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(
     this IEnumerable<T> source, Func<T, T, bool> predicate) 
    { 
     using (var e = source.GetEnumerator()) 
     { 
      if (e.MoveNext()) 
      { 
       var list = new List<T> { e.Current }; 
       var pred = e.Current; 
       while (e.MoveNext()) 
       { 
        if (predicate(pred, e.Current)) 
        { 
         list.Add(e.Current); 
        } 
        else 
        { 
         yield return list; 
         list = new List<T> { e.Current }; 
        } 
        pred = e.Current; 
       } 
       yield return list; 
      } 
     } 
    } 
} 
+1

Gracias, esto ahorró mucho tiempo. – nRk

1

¿Cuál es el algoritmo que desea implementar? Averigüe qué quiere que suceda, luego vea si podría hacerse más claro con una traducción LINQ. Aquí hay algo que no es LINQ que podría darte una idea.

int[] array = { 1, 2, 3, 4, 7, 8, 11, 15, 16, 17, 18}; 
List<string> ranges = new List<string>(); 

// code assumes array is not zero-length, is distinct, and is sorted. 
// to do: handle scenario as appropriate if assumptions not valid 

Action<int, int, List<string>> addToRanges = (first, last, list) => 
{ 
    if (last == first) 
     list.Add(last.ToString()); 
    else 
     list.Add(string.Format("{0}-{1}", first, last)); ; 
}; 

int firstItem = array[0]; 
int lastItem = firstItem; 
foreach (int item in array.Skip(1)) 
{ 
    if (item > lastItem + 1) 
    { 
     addToRanges(firstItem, lastItem, ranges); 
     firstItem = lastItem = item; 
    } 
    else 
    { 
     lastItem = item; 
    } 
} 

addToRanges(firstItem, lastItem, ranges); 

// return ranges or ranges.ToArray() 
+0

Quiero implementar lo que es más claro, estaba intentando algo con linq group pero sin éxito – Alexandre

4

No es necesario LINQ; de hecho, la solución más fácil requiere conocer tres posiciones en la matriz (su número inicial, número actual y el siguiente número después de la actual), para los cuales los Enumerables no son adecuados.

Prueba esto:

var start = 0; 
var end = 0; 
var write = false; 
var builder = new StringBuilder(); 
for(var i=0; i<array.Length; i++) 
{ 
    //arranged this way to avoid ArrayOutOfBoundException 
    //if the next index doesn't exist or isn't one greater than the current, 
    //the current index is the end of our incremental range. 
    if(i+1 == array.Length || array[i+1] > array[i] + 1) 
    { 
     end = i; 
     write = true; 
    } 

    if(write) 
    { 
     if(end - start == 0) //one number 
     builder.Append(String.Format("{0}, ", array[start]); 
     else //multi-number range 
     builder.Append(String.Format("{0}-{1}, ", array[start], array[end]); 

     start = i+1; 
     end = i+1; //not really necessary but avoids any possible case of counting backwards 
     write = false; 
    } 

} 

puede reorganizar esto para reducir la anidación de código, continue principios de la lógica de bucle, y quitar unos VARs; ganarás unos mil millones de tiempo de ejecución. También deberá recortar los dos últimos caracteres (una coma y un espacio al final) del extremo del StringBuilder antes de sacar el String.

+0

_¿No necesita Linq_? Si bien su versión es totalmente correcta, una solución ideológica LINQ también tiene la ventaja de que puede portar fácilmente una solución de este tipo a cualquier lenguaje de programación funcional, como F # o Haskell. – mbx

1

Aquí es un corte en él:

public static IEnumerable<string> ToRanges(this IEnumerable<int> values) 
{ 
    int? start = null, end = null; 
    foreach (var value in values.OrderBy(vv => vv)) 
    { 
     if (!start.HasValue) 
     { 
      start = value; 
     } 
     else if (value == (end ?? start) + 1) 
     { 
      end = value; 
     } 
     else 
     { 
      yield return end.HasValue 
       ? String.Format("{0}-{1}", start, end) 
       : String.Format("{0}", start); 
      start = value; 
      end = null; 
     } 
    } 

    if (start.HasValue) 
    { 
     yield return end.HasValue 
      ? String.Format("{0}-{1}", start, end) 
      : String.Format("{0}", start); 
    } 
} 
Cuestiones relacionadas