我想根据两个条件对点云进行分组
-
在Y上很简单,所以我写了
pointcloudH.GroupBy(KVP => KVP.Value.Y)
,其中KVP是KeyValuePair<string,System.Drawing.Point>
-
现在我想把它也按X分组,如果
X == (previousX + 1)
据我所知,我应该是ThenBy()
,但我必须在括号之间写些什么?
这里有一个例子可以更好地说明我想要实现
样本点云
(x|y) (1|1),(2|1),(4|1),(1|2),(2|3),(3|3),(4|3),(5|8),(9|10)
在步骤1之后。看起来像这个
group1 (1|1),(2|1),(4|1)
group2 (1|2)
group3 (2|3),(3|3),(4|3)
group4 (5|8)
group5 (9|10)
在步骤2之后。它应该看起来像这个
group1 (1|1),(2|1)
group2 (4|1)
group3 (1|2)
group4 (2|3),(3|3),(4|3)
group5 (5|8)
group6 (9|10)
当前代码
var Hgroup = pointcloudH.OrderBy(KVP => KVP.Value.Y) // order by Y
.GroupBy(KVP => KVP.Value.Y) // groub by Y
.ThenBy(KVP => KVP.Value.X); // group by X ???
我不认为LINQ是这类工作的最佳工具,但它是可以实现的。重要的部分是考虑你的Point.X
和Point.Y
组中相对Point
的指数之间的关系。一旦你意识到你想用Point.X - Index
对它们进行分组,你就可以做:
var Hgroup = pointcloudH.OrderBy(p => p.Y)
.GroupBy(p => p.Y)
.SelectMany(yGrp =>
yGrp.Select((p, i) => new {RelativeIndex = p.X - i, Point = p})
.GroupBy(ip => ip.RelativeIndex, ip => ip.Point)
.Select(ipGrp => ipGrp.ToList()))
.ToList();
请注意,这可能比常规迭代算法执行得最差。我的pointcloudH是一个数组,但您可以更改lambda以反映您自己的列表。此外,如果要推迟执行,请删除ToList()
。这是为了简化调试器中的结果检查。
如果您想将Point.Y
组中的所有点分组,而不考虑它们的索引(即按Point.X
排序)。在第一个OrderBy
子句后添加ThenBy(p => p.X)
。
您的问题不能通过执行两个单独的分组子句来解决。我创建了一个小样本,应该可以解决你的问题。这些是代码中正在发生的关键事情:
- 构造"mirror"数组并在索引0处插入第一个项的副本,这用于跟踪前一点
-
创建一个变量,该变量在"链"断开时递增。每当下一个值不等于上一个+1时,就会出现这种情况。通过这种方式,我们可以按每个"链"的唯一键进行分组。
班级计划{公共结构点{公共静态点创建(int x,int y){返回new Point(){X=X,Y=Y};}
public int X { get; set; } public int Y { get; set; } public override string ToString() { return string.Format("({0}|{1})", X, Y); } } static void Main(string[] args) { //helper to avoid to much keystrokes :) var f = new Func<int, int, Point>(Point.Create); //compose the point array //(1|1),(2|1),(4|1),(1|2),(2|3),(3|3),(4|3),(5|8),(9|10) var points = new[] { f(1, 1), f(2, 1), f(4, 1), f(1, 2), f(2, 3), f(3, 3), f(4, 3), f(5, 8), f(9, 10) }.OrderBy(p => p.Y).ThenBy(p => p.X);; //create a 'previous point' array which is a copy of the source array with a item inserted at index 0 var firstPoint = points.FirstOrDefault(); var prevPoints = new[] { f(firstPoint.X - 1, firstPoint.Y) }.Union(points); //keep track of a counter which will be the second group by key. The counter is raised whenever the previous X was not equal //to the current - 1 int counter = 0; //the actual group by query var query = from point in points.Select((x, ix) => new { current = x, prev = prevPoints.ElementAt(ix) }) group point by new { point.current.Y, prev = (point.prev.X == point.current.X - 1 ? counter : ++counter) }; //method chaining equivalent query = points.Select((x, ix) => new { current = x, prev = prevPoints.ElementAt(ix) }) .GroupBy(point => new { point.current.Y, prev = (point.prev.X == point.current.X - 1 ? counter : ++counter) }); //print results foreach (var item in query) Console.WriteLine(string.Join(", ", item.Select(x=> x.current))); Console.Read(); }
}