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

  • 本文关键字:Linq c# linq linq-group
  • 更新时间 :
  • 英文 :


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

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

我不认为LINQ是这类工作的最佳工具,但它是可以实现的。重要的部分是考虑你的Point.XPoint.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();
    }
    

    }

相关内容

  • 没有找到相关文章

最新更新