2

我想根据 2 个条件对点云进行分组

  1. 在 Y 上很简单,所以我写pointcloudH.GroupBy(KVP => KVP.Value.Y)了 KVP 是KeyValuePair<string,System.Drawing.Point>

  2. 现在我也想按 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 ???
4

2 回答 2

2

我不认为 LINQ 是这种工作的最佳工具,但它可以实现。重要的部分是考虑您与组中亲属的索引之间的Point.X关系。一旦您意识到要按 对它们进行分组,您可以执行以下操作:PointPoint.YPoint.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分组,而不考虑它们的索引(即 order byPoint.X以及。ThenBy(p => p.X)在第一个OrderBy子句之后添加。

于 2013-08-09T13:18:03.570 回答
0

您的问题无法通过执行 2 个单独的 group by 子句来解决。我创建了一个小样本,应该可以解决您的问题。这些是代码中发生的关键事情:

  • 构造“镜像”数组并在索引 0 处插入第一项的副本,这用于跟踪前一点
  • 创建一个在“链”中断时递增的变量。这是每当下一个值不等于前一个 + 1 时。这样我们可以按每个“链”的唯一键进行分组。

    类程序 { public struct Point { public static Point Create(int x, int y) { return 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();
    }
    

    }

于 2013-08-09T13:21:02.820 回答