如何根据以前的值在Linq中分组

时间:2013-08-09 12:16:05

标签: c# linq linq-group

我想基于2个条件对pointcloud进行分组

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

  2. 现在我想通过X对其进行分组X == (previousX + 1) 据我所知,我应该ThenBy(),但我必须在括号之间写一下?

  3. 这里有一个例子,可以更好地说明我想要实现的目标

    示例点云

    (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 ???
    

2 个答案:

答案 0 :(得分:2)

我不认为LINQ是这类工作的最佳工具,但它可以实现。重要的是要考虑Point.X组与Point组中相对Point.Y的索引之间的关系。一旦您意识到要按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排序。)在第一个ThenBy(p => p.X)子句后添加OrderBy

答案 1 :(得分:0)

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

  • 构造'mirror'数组并在索引0处插入第一个项目的副本,这用于跟踪前一个点
  • 创建一个变量,只要“链”被破坏,该变量就会递增。这是每当下一个值不等于前一个+ 1时。这样我们就可以按每个'链'的唯一键进行分组。

    课程计划 {     public struct Point     {         public static Point Create(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();
    }
    

    }