我想根据条件及其在列表中的顺序对某些数字进行聚类。
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)对它们进行分组,但每个组应该只包含第一个列表中彼此相邻的数字。
答案 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>>