C#将时间列表拆分为时间范围

C#将时间列表拆分为时间范围,c#,.net,linq,list,C#,.net,Linq,List,我希望使用linq从整数列表中提取范围: 例如,我希望拆分以下列表: List<int> numberList = new List<int>() { 30, 60, 90, 120, 150, 180, 270, 300, 330 }; ie:下一个seq大于30 另一个例子: List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 330 }; 我尝试

我希望使用linq从整数列表中提取范围:

例如,我希望拆分以下列表:

List<int> numberList = new List<int>() { 30, 60, 90, 120, 150, 180, 270, 300, 330 };  
ie:下一个seq大于30

另一个例子:

List<int> numberList = new List<int>() { 30, 60, 120, 150, 270, 300, 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,120,150}
将返回:

{ 30, 30 }
{ 120, 150 }

您可以使用
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();
    }
}
当前代码至少需要列表中的一个元素,如果您有

{ 30, 100 }
它将返回两个列表,每个列表中有一个元素

{ 30 }
{ 100 }

你必须使用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);
}
List numberList=newlist(){30,60,120,150,270,300,330};
字典结果=新字典();
int lastStart=numberList.First();
for(int i=1;i=lastStart+30)
{
结果.Add(lastStart,numberList[i]);
如果(i==numberList.Count-1)中断;
lastStart=numberList[i+1];
i++;
}
}
foreach(结果中的var项目)
{
WriteLine(“{{{0},{1}}”,item.Key,item.Value);
}
试试这个:

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();
}
private static List GetGroups(List numberList)
{
列表组=新列表();
numberList.Zip(numberList.Skip(1),(a,b)=>
{
如果((b-a)==30)
{
如果(groups.Count==0)
添加(新列表());
组[groups.Count-1]。添加(a);
}
如果(a==b),则为else
{
组[groups.Count-1]。添加(a);
}
其他的
{
组[groups.Count-1]。添加(a);
添加(新列表());
}
返回a;
}).ToList();
组[groups.Count-1].Add(numberList.Last());
返回组。选择(g=>new[]{g.First(),g.Last()});
}
示例用法:

//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);
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 }
//List numberList=newlist(){30,60,90,120,150,180,270,300,330};
List numberList=newlist(){30,60,120,150,270,300,330};
var结果=GetGroups(numberList);

您可以在一条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();
var numberList=newlist(){30,60,120,150,270,300,330};
var段=0;
var结果=数字列表
.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;
}
公共静态IEnumerable ToRanges(
此IEnumerable源,Func isNear)
{            
列表结果=源。/*OrderBy(value=>value)。*/Aggregate(
新列表{new[]{source.First(),source.First()},
(范围,当前值)=>{
TSource[]currentRange=ranges.Last();
TSource previousValue=当前范围[1];
如果(isNear(currentRange[0],previousValue,currentValue))
currentRange[1]=当前值;
其他的
添加(新[]{currentValue,currentValue});
返回范围;
}
);
返回结果;
}
用法示例:

//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);
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 }
List number=newlist(){30,60,90,120,150,180,270,300,330};
//按最大差值分割
数字列表ToRanges(

(first,previous,current)=>current-previous current-first你能更清楚地说明你是如何选择要提取的序列的吗?他的意思是当一个元素比前一个元素大30以上时,一个新的范围就开始了。你在乎你是通过for循环还是linq来实现这一点吗?你会对给定的范围做什么{30,100,200}?会是{30,30},{100,100}等等吗?如果我不够具体,那很抱歉,这正是@hatchet所说的——只要一个元素大于30,那么它就标志着一个范围的结束和下一个范围的开始。这很好,但它确实会改变输入(所以你可能想要复制),而且效率不高(由于有多个
removange
调用)…@ReedCopsey我改为
Skip
而不是removingWow-这对我来说很有用-我特别喜欢添加差异参数!抱歉-第一次使用stackoverflow-在接受回答之前等待24小时是礼节吗?@Wattchey否-但我(个人)我想给人们20-30分钟的时间,让他们至少在第一时间得到答案。谢谢,我会在未来留出更多的时间。雄辩-我可能会按照你的建议,将此用于DateTime/Timespans!
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 }