问题描述
给定以下数字(代表星期几):1,2,3,4,5,6,7.
以下是一些组合示例及其所需的输出:
- 1,2,3,5,6,7 -> 1-3,5-7
- 1,3,5,7 -> 1,3,5,7
- 1,2,5,6 -> 1,2,5,6
- 1,2,3,6,7 -> 1-3,6,7
这个想法是,连续 3 天或更多天成为一个范围,而单独呈现单个或非后续天(或者从 2 开始制作范围更好).
我不知道应该从哪里开始编写一个复杂的 ifed 函数,或者这可以使用 LINQ 函数之一来完成?
任何多汁的建议?
我使用数字来简化范围的概念,但在我的代码中,我声明了一个标记的枚举,如下所示:
[Flags] public enum DaysOfWeek { Sunday = 0x1, Monday = 0x2, Tuesday = 0x4, Wednesday = 0x8, Thursday = 0x10, Friday = 0x20, Saturday = 0x40 }
我有一个带有 DaysOfWeek 字段的实体 OpeningTimes,它说明该实体的小时范围(在另一个属性中定义)适用于一周中的哪几天.
所以得到我使用的上述内容(要真正获得数字,我会使用索引 + 1 添加 Select):
var days = Enum.GetValues(typeof(DaysOfWeek)) .Cast<DaysOfWeek>() .Where(dow => Model.DaysOfWeek.HasFlag(dow));
我认为这个想法是首先删除一个范围内的数字.
我相信我正在寻找一个聚合函数,它也接收前一个值,并且可以返回另一个值类型,所以我可以创建一个函数,如果当前值 -1 等于 prev.值,我等待下一个值,直到范围不连续(或者如果元素代表自身),这就是当我将最后一个批量作为匿名对象返回并开始处理新对象时.
然后我会做一个格式化函数,上面写着 if (item.First != item.Last) string.Join("-", item.First, Item.Last);
推荐答案
有趣的问题.为了可读性,我决定使用一个表示范围的类:
class NumberRange { public int Start { get; set;} public int End { get; set;} public override string ToString() { return Start == End ? Start.ToString() : String.Format("{0}-{1}",Start,End); } }
以及将有序整数的 IEnumerable 转换为范围的 IEnumerable 的扩展方法:
public static IEnumerable<NumberRange> ToRanges(this IEnumerable<int> numbers) { NumberRange currentRange = null; foreach(var number in numbers) { if (currentRange == null) currentRange = new NumberRange() { Start = number, End = number }; else if (number == currentRange.End + 1) currentRange.End = number; else { yield return currentRange; currentRange = new NumberRange { Start = number, End = number }; } } if (currentRange != null) { yield return currentRange; } }
有了它,您就可以获得范围并根据需要对其进行格式化:
String.Join(",", new int[] { 1,2,3,5,7,8,9,11 } .ToRanges() .Select(r => r.ToString()))
其他推荐答案
我是这么想的:
void Main() { Console.WriteLine(AggregateString(new int[]{1,2,3,5,6,7})); //1-3,5-7 Console.WriteLine(AggregateString(new int[]{1,3,5,7})); //1,3,5,7 Console.WriteLine(AggregateString(new int[]{1,2,5,6})); //1,2,5,6 Console.WriteLine(AggregateString(new int[]{1,2,3,6,7 })); //1-3,6,7 } string AggregateString(int[] ary) { List<List<int>> result=new List<List<int>>(); ary.Aggregate((m,n)=> { if(m == n-1) { if(result.LastOrDefault()!=null && result.LastOrDefault().Last() ==m) result.Last().Add(n); else result.Add(new List<int>{m,n}); } else { if(result.LastOrDefault()==null) result.Add(new List<int>{m,n}); else result.Add(new List<int>{n}); } return n; }); return string.Join(",", result.Select(s=>s.Count()>2? string.Join("-",new string[]{s.First().ToString(),s.Last().ToString()}) : string.Join(",",s.Select(x=>x.ToString()).ToArray())).ToArray()); }
其他推荐答案
这是我的看法.(不幸的是,我无法阻止复制一个部分:
static IEnumerable<string> GetRange(IEnumerable<int> range) { using(IEnumerator<int> iter = range.GetEnumerator()) if(iter.MoveNext()) { int last = iter.Current; int start = iter.Current; while(iter.MoveNext()) { int curr = iter.Current; if (curr == last+1) { last = curr; continue; } // found gap if (start == last) // one isolated value { yield return start.ToString(); } else if (last - start == 1) // two in a row. { yield return start.ToString(); yield return last.ToString(); } else { yield return string.Format("{0}-{1}", start,last); } start = curr; last = curr; } if (start == last) // one isolated value { yield return start.ToString(); } else if (last - start == 1) // two in a row. { yield return start.ToString(); yield return last.ToString(); } else { yield return string.Format("{0}-{1}", start,last); } } }
问题描述
Given the following numbers (representing days of week): 1,2,3,4,5,6,7.
Here are some combination examples and their desired output:
- 1,2,3,5,6,7 -> 1-3,5-7
- 1,3,5,7 -> 1,3,5,7
- 1,2,5,6 -> 1,2,5,6
- 1,2,3,6,7 -> 1-3,6,7
The idea is that 3 or more consecutive days become a range while single or non-following days are rendered separately (or is it nicer to make range starting from 2).
I don't know where to start should I write a complicated ifed function or this can be done with one of the LINQ functions?
Any juicy suggestions?
I used numbers to simplify the idea of ranges, but in my code I have an flagged enum declared as follows:
[Flags] public enum DaysOfWeek { Sunday = 0x1, Monday = 0x2, Tuesday = 0x4, Wednesday = 0x8, Thursday = 0x10, Friday = 0x20, Saturday = 0x40 }
I have an entity OpeningTimes with a field DaysOfWeek, that tells what days in week the hour-ranges (defined in another property) of this entity apply to.
So the get the above I use (to really get numbers I'd add Select using index + 1):
var days = Enum.GetValues(typeof(DaysOfWeek)) .Cast<DaysOfWeek>() .Where(dow => Model.DaysOfWeek.HasFlag(dow));
I think the idea is to first remove the numbers within a range.
I believe I'm looking for an aggregation function that receives the previous value as well, and can return another value-type, so I can make a function that if current value -1 equals prev. value, I wait for the next value, until range is not consecutive (or if element stands for itself) which is when I yield return the last bulk as an anonymous object and start working on the new one.
Then I'll make a formatting function that says if (item.First != item.Last) string.Join("-", item.First, Item.Last);
推荐答案
Interesting problem. I decided for readability to have a class representing a range:
class NumberRange { public int Start { get; set;} public int End { get; set;} public override string ToString() { return Start == End ? Start.ToString() : String.Format("{0}-{1}",Start,End); } }
and an extension method to turn an IEnumerable of ordered integers into an IEnumerable of ranges:
public static IEnumerable<NumberRange> ToRanges(this IEnumerable<int> numbers) { NumberRange currentRange = null; foreach(var number in numbers) { if (currentRange == null) currentRange = new NumberRange() { Start = number, End = number }; else if (number == currentRange.End + 1) currentRange.End = number; else { yield return currentRange; currentRange = new NumberRange { Start = number, End = number }; } } if (currentRange != null) { yield return currentRange; } }
And with that in place you can get the ranges and format them however you want:
String.Join(",", new int[] { 1,2,3,5,7,8,9,11 } .ToRanges() .Select(r => r.ToString()))
其他推荐答案
Here is what I thought:
void Main() { Console.WriteLine(AggregateString(new int[]{1,2,3,5,6,7})); //1-3,5-7 Console.WriteLine(AggregateString(new int[]{1,3,5,7})); //1,3,5,7 Console.WriteLine(AggregateString(new int[]{1,2,5,6})); //1,2,5,6 Console.WriteLine(AggregateString(new int[]{1,2,3,6,7 })); //1-3,6,7 } string AggregateString(int[] ary) { List<List<int>> result=new List<List<int>>(); ary.Aggregate((m,n)=> { if(m == n-1) { if(result.LastOrDefault()!=null && result.LastOrDefault().Last() ==m) result.Last().Add(n); else result.Add(new List<int>{m,n}); } else { if(result.LastOrDefault()==null) result.Add(new List<int>{m,n}); else result.Add(new List<int>{n}); } return n; }); return string.Join(",", result.Select(s=>s.Count()>2? string.Join("-",new string[]{s.First().ToString(),s.Last().ToString()}) : string.Join(",",s.Select(x=>x.ToString()).ToArray())).ToArray()); }
其他推荐答案
Here's my take on it. (Unfortunately, I couldn't prevent duplicating one section:
static IEnumerable<string> GetRange(IEnumerable<int> range) { using(IEnumerator<int> iter = range.GetEnumerator()) if(iter.MoveNext()) { int last = iter.Current; int start = iter.Current; while(iter.MoveNext()) { int curr = iter.Current; if (curr == last+1) { last = curr; continue; } // found gap if (start == last) // one isolated value { yield return start.ToString(); } else if (last - start == 1) // two in a row. { yield return start.ToString(); yield return last.ToString(); } else { yield return string.Format("{0}-{1}", start,last); } start = curr; last = curr; } if (start == last) // one isolated value { yield return start.ToString(); } else if (last - start == 1) // two in a row. { yield return start.ToString(); yield return last.ToString(); } else { yield return string.Format("{0}-{1}", start,last); } } }