使用LINQ将一个数字序列无间隙地分组
用这个数组int[]{ 1, 2, 3, 4, 7, 8, 11, 15,16,17,18 };
我怎样才能转换为这个string数组"1-4","7-8","11","15-18"
build议? 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)));
同
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; } } } }
你不需要Linq; 实际上,最简单的解决scheme需要知道arrays中的三个位置(您的起始编号,当前编号和当前的下一个编号),Enumerables并不适合。
尝试这个:
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; } }
你可以重新排列这个以减less代码的嵌套,在循环逻辑的早期continue
,并删除几个variables; 你将获得几毫米的执行时间。 在得到String之前,还需要修剪StringBuilder末尾的最后两个字符(拖尾的逗号和空格)。
你想要实现的algorithm是什么? 找出你想要发生的事情,然后看看LINQ翻译是否可以使它更清晰。 这是非LINQ可以给你一个想法。
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()
这是一个切入点:
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); } }