Use LINQ para agrupar una secuencia de números sin espacios vacíos

Con esta matriz int[]{ 1, 2, 3, 4, 7, 8, 11, 15,16,17,18 }; ¿Cómo puedo convertir a este conjunto de cadenas "1-4","7-8","11","15-18"

Sugerencias? Linq?

 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> GroupAdjacentBy( this IEnumerable source, Func predicate) { using (var e = source.GetEnumerator()) { if (e.MoveNext()) { var list = new List { 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 { e.Current }; } pred = e.Current; } yield return list; } } } } 

No necesitas 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[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 reordenar esto para reducir la anidación del código, continue temprano en la lógica de bucle y eliminar algunas variables; 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.

¿Cuál es el algoritmo que quieres 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 ranges = new List(); // code assumes array is not zero-length, is distinct, and is sorted. // to do: handle scenario as appropriate if assumptions not valid Action> 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() 

Aquí hay un corte en esto:

 public static IEnumerable ToRanges(this IEnumerable 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); } }