用LINQ挑战管理范围
问题描述:
给定以下数字(表示星期几):1,2,3,4,5,6,7
。用LINQ挑战管理范围
下面是一些组合的实施例和它们的期望输出:
-
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开始)。
我不知道从哪里开始,我应该写一个复杂的if
ed函数,或者这可以通过LINQ函数之一完成?
有没有多汁的建议?
我用数字来简化范围的想法,但在我的代码我有一个标记的枚举声明如下:
[Flags]
public enum DaysOfWeek
{
Sunday = 0x1,
Monday = 0x2,
Tuesday = 0x4,
Wednesday = 0x8,
Thursday = 0x10,
Friday = 0x20,
Saturday = 0x40
}
我有一个实体OpeningTimes
与现场DaysOfWeek
,告诉什么天一周此实体的小时范围(在另一个属性中定义)适用于。
所以得到我用上面的(真正得到的数字我会使用索引+ 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);
}
}
}
你也可以使用锯齿阵列 – Shimmy
锯齿阵列是不好的linq。所以我更喜欢List。 :) –
稍后我会调整你的代码,但我认为这是我要使用的。 – Shimmy