c#拆分时间列表到时间范围
本文关键字:时间 范围 列表 拆分 | 更新日期: 2023-09-27 18:16:51
我希望使用linq:
从整数列表中提取范围以为例,我希望拆分以下列表:
List<int> numberList = new List<int>() { 30, 60, 90, 120, 150, 180, 270, 300, 330 };
将转换为整数范围列表,如下所示:
{ 30, 180 }
{ 270, 330 }
即:下一个序列大于30
另一个例子:
List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 };
将转换为整数范围列表,如下所示:
{ 30, 60 }
{ 120, 150 }
{ 270, 330 }
我已经尝试用for循环找到最好的方法,但我没有知道从哪里开始尝试使用linq查询来完成此操作
您可以编写一个方法来处理分割:
IEnumerable<IList<int>> SplitValues(IList<int> input, int difference = 30)
{
List<int> results = new List<int>();
int last = input.First();
foreach(var value in input)
{
if (value - last > difference)
{
yield return new[] {results.First(), results.Last()};
results = new List<int>();
}
results.Add(value);
last = value;
}
yield return new[] {results.First(), results.Last()};
}
与描述的规格匹配,返回:
{ 30, 60 }
{ 120, 150 }
{ 270, 330 }
注意,集合中没有范围的单个值将被重复。例如,{ 30, 120, 150 }
将返回:
{ 30, 30 }
{ 120, 150 }
您可以在一个linq语句中完成:
var numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 };
var section = 0;
var result = numberList
.Select( (x, i) => new {value = x, section = (i == 0 ? 0 : ((x - numberList[i - 1]) > 30 ? ++section : section))})
.GroupBy(x => x.section)
.Select(x => x.Select(v => v.value).ToList()).ToList();
好吧。有很多方法可以做到这一点,所有的方法都有其利弊。这是另一个解决方法,希望对大家有所帮助。
public static IEnumerable<TSource[]> ToRanges<TSource>(
this IEnumerable<TSource> source, Func<TSource, TSource, TSource, bool> isNear)
{
List<TSource[]> result = source./*OrderBy(value => value).*/Aggregate(
new List<TSource[]> { new[] { source.First(), source.First() } },
(ranges, currentValue) => {
TSource[] currentRange = ranges.Last();
TSource previousValue = currentRange[1];
if (isNear(currentRange[0], previousValue, currentValue))
currentRange[1] = currentValue;
else
ranges.Add(new[] { currentValue, currentValue});
return ranges;
}
);
return result;
}
使用例子:
List<int> numbers = new List<int>() { 30, 60, 90, 120, 150, 180, 270, 300, 330 };
// split by max difference
numberList.ToRanges(
(first, previous, current) => current - previous <= 30).ToArray();
// { 30, 180 }
// { 270, 330 }
// split by max range
numberList.ToRanges(
(first, previous, current) => current - first <= 90).ToArray();
// { 30, 120 }
// { 150, 180 }
// { 270, 330 }
此外,您不仅可以分割整数,还可以分割单词的首字母。或者是DateTime
/TimeSpan
。
必须使用LINQ?如果没有,那么:
List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 };
Dictionary<int, int> result = new Dictionary<int, int>();
int lastStart = numberList.First();
for(int i=1; i < numberList.Count; i++)
{
if(numberList[i] >= lastStart + 30)
{
result.Add(lastStart, numberList[i]);
if (i == numberList.Count - 1) break;
lastStart = numberList[i + 1];
i++;
}
}
foreach (var item in result)
{
Console.WriteLine("{{{0}, {1}}}", item.Key, item.Value);
}
您可以使用TakeWhile
并将结果添加到另一个列表
void SplitByRange()
{
List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 };
IEnumerable<int> aux = new List<int>();
int n = numberList.First();
int skip = 0;
List<List<int>> output = new List<List<int>>();
while ((aux = numberList.Skip(skip).TakeWhile(o => { bool r = (o - n) <= 30; n = o; return r; })).Count() > 0)
{
output.Add(aux.ToList());
skip += aux.Count();
}
}
最后numberList
将为空,output
将是一个列表的列表。
output[0] // { 30, 60 }
...
当前代码要求列表中至少有一个元素,如果你有
{ 30, 100 }
返回两个列表,每个列表中有一个元素
{ 30 }
{ 100 }
试试这个:
private static List<int[]> GetGroups(List<int> numberList)
{
List<List<int>> groups = new List<List<int>>();
numberList.Zip(numberList.Skip(1), (a, b) =>
{
if ((b - a) == 30)
{
if (groups.Count == 0)
groups.Add(new List<int>());
groups[groups.Count - 1].Add(a);
}
else if (a == b)
{
groups[groups.Count - 1].Add(a);
}
else
{
groups[groups.Count - 1].Add(a);
groups.Add(new List<int>());
}
return a;
}).ToList();
groups[groups.Count - 1].Add(numberList.Last());
return groups.Select(g => new[] { g.First(), g.Last() }).ToList();
}
示例用法://List<int> numberList = new List<int>() { 30, 60, 90, 120, 150, 180, 270, 300, 330 };
List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 };
var result = GetGroups(numberList);
int bin = 15;
var start = DateTime.Parse("08:00:00");
var end = DateTime.Parse("09:00:00");
var timeFrame = Enumerable
.Range(0, Convert.ToInt32((end.TimeOfDay.TotalMinutes - start.TimeOfDay.TotalMinutes) / bin))
.Select((s,i) => start.AddMinutes(i * bin)).ToList();
foreach (var t in timeFrame)
{
Console.WriteLine($"T: {t}");
}