按条件和顺序使用linq对项目进行集群

时间:2014-01-10 20:16:10

标签: c# linq grouping

我想根据条件及其在列表中的顺序对某些数字进行聚类。

int delta = 3;    
var numbers = new List<int>() { 2, 4, 9, 6, 3, 2, 7, 7, 4, 1, 9, 1, 2 };    
var g = numbers.GroupBy(n => n <= delta);

根据条件给出两组。我想要的是:

g1: 2
g2: 4, 9, 6
g3: 3, 2
g4: 7, 7, 4
g5: 1
g6: 9
g7: 1, 2

修改 条件是根据条件(这里是数字&lt; = delta)对它们进行分组,但每个组应该只包含第一个列表中彼此相邻的数字。

3 个答案:

答案 0 :(得分:4)

如果我理解逻辑,那么只要项目n通过条件但项目n - 1失败,您就要创建一个新组,反之亦然。

好吧,通常你不会将Linq用于这样的事情。您必须一次遍历循环一个项目并自己构建结果集。例如:

List<int> list = null;
var result = new List<IEnumerable<int>>();
bool? prev = null;
foreach (var n in numbers)
{
    bool cur = n <= 3;
    if (prev != cur)
    {
        list = new List<int>();
        result.Add(list);
        prev = cur;
    }
    list.Add(n);
}

但Linq是一个可行的解决方案。这取决于您通常应避免的场地效应:

var prev = numbers.First() <= delta;
var counter = 0;
var result = numbers.GroupBy(n => (prev != (prev = n <= delta)) ? ++counter : counter)
                    .ToList();

答案 1 :(得分:3)

因此,我们在概念上所做的是在满足条件的情况下浏览列表并进行分组。我们可以毫不费力地为此编写相应的操作:

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
    this IEnumerable<T> source, Func<T, T, bool> predicate)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
            yield break;

        List<T> list = new List<T>() { iterator.Current };

        T previous = iterator.Current;

        while (iterator.MoveNext())
        {
            if (!predicate(previous, iterator.Current))
            {
                yield return list;
                list = new List<T>();
            }

            list.Add(iterator.Current);
            previous = iterator.Current;
        }
        yield return list;
    }
}

我们现在可以写:

var groups = numbers.GroupWhile((prev,next) => 
    (prev <= delta) == (next <= delta));

此处开始新组的条件是前一项的比较与当前项的比较。

答案 2 :(得分:0)

如果您是fold的粉丝,可以这样写:

var groups = numbers.Skip(1).Aggregate(new List<List<int>>(new[] { new List<int> { numbers[0] } }), (acc, b) =>
{
    if ((acc.Last().LastOrDefault() <= delta) == (b <= delta))
    {
        acc.Last().Add(b);
    }
    else
    {
        acc.Add(new List<int>() { b });
    }
    return acc;
})

此处的群组类型为List<List<int>>