用Linq重新排列嵌套列表

本文关键字:排列 嵌套 列表 Linq 新排列 | 更新日期: 2023-09-27 18:14:14

我目前正在做一个项目,我从电子表格中读取行数据。基础数据的结构可以在左侧的附图中看到。也可以看出,我想分两步重新排列数据。

Image1:排序列表的步骤

  • 首先,我将元素与相同的PartName组合在一起,并将两个坐标对组合在一行上。我将坐标存储在嵌套列表中。
  • 其次,我想将元素与相同的ID合并,并再次合并坐标。重要的是,列表保持其顺序,并且最终不存在重复项。{x1y1,x2y2}{x2y2,x3y3}=>{x1y1,x2y2,x3y3} .

元素部分的数量是泛型的,因此也是坐标对的数量。

下面是我目前正在使用的简化的对象类:

public class ElmLine
{
    public int Id{ get; set; }
    public string PartName {get; set;}
    public List<CoordList> CoordList { get; set; }
}
public class CoordList
{
    public decimal XCord { get; set; }
    public decimal YCord { get; set; }
}

这是步骤1的方法。我重新排列了基本列表,并将协调对存储在名为CoordList的嵌套列表中。

public void BuildLinesFormSegments(IList<ElmLine> filteredLine)
    {
        // Merge line parts and add FROM and TO to each line part 
        var combinedLineParts = filteredLine
            .GroupBy(c => new { c.Fid, c.FidPart }).Select(g => new ElmLine()
            {
                Id = g.Select(c => c.Id).First(),
                PartName = g.Select(c => c.PartName).First(),
                CoordList = g.Select(c => new CoordList() 
                            { XCord = c.CoordX, YCord = c.CoordY }).ToList(),
            }).ToList();
    }
结果列表combinedLineParts如下所示:
var list = new LineParts<ElmLine>
{
    new ElmLine {Name = 1, CoordList = new List<CoordList>
        {new CoordList {XCord = x1, YCord = y1}, new CoordList { XCord = x2, YCord = y2 }} },
    new ElmLine {Name = 1, CoordList = new List<CoordList>
        {new CoordList {XCord = x2, YCord = y2}, new CoordList { XCord = x3, YCord = y3 }} },
    new ElmLine {Name = 2, CoordList = new List<CoordList>
        {new CoordList {XCord = x11, YCord = y11}, new CoordList { XCord = x12, YCord = y12 }} },
    new ElmLine {Name = 2, CoordList = new List<CoordList>
        {new CoordList {XCord = x12, YCord = y12}, new CoordList { XCord = x13, YCord = y13 }} },
};

不幸的是,我被困在这一点上,我没有好主意继续进行步骤2。如果有人能给我一个提示,我下一步该做什么,那就太好了。

提前感谢。

用Linq重新排列嵌套列表

步骤3确实不需要依赖于步骤2。你只是按ID分组并保持不同的坐标值:

var result = filteredLine
        .GroupBy(c => c.Fid)
        .Select(g => new ElmLine()
        {
            Id = g.Key,
            CoordList = g
                .Select(c => { c.CoordX, c.CoordY })
                .Distinct()
                .Select(c => new CoordList() 
                        { XCord = c.CoordX, YCord = c.CoordY })
                .ToList(),
        }).ToList();

通过大量的思考和StriplingWarrior的提示,我能够解决我的问题。为了完成这篇文章,我想展示一下我是如何做到的。

评论行中的### Parts ###是对初始帖子中image1的引用。我在这三个步骤中解决了我的问题,可以看到我在每一步中使用我的数据所做的工作。

为了更好地理解我在做什么,你可以想象多米诺骨牌。每条石链由一定数量的石头组成,这些石头位于基本数据中,而不是在一行中。

基础数据:{1,4} {1,3} {2,9} {2,7} {3,6} {3,3} {4,4} {4,7}(每一行{StoneNr,SideValue}为半块石头)

第1部分结果:[4|3] [9|7] [6|3] [4|7](宝石组合列表)

第二部分的结果:[9|7] [7|4] [4|3] [3|6](一个重新排列的列表,所有对都像多米诺骨牌链一样紧挨着)

第三部分的结果:{9,7,4,3,6}(一个数字按顺序排列的字符串)

事实上,石头值是坐标,因此成对的数字和石头链是2d平面中的直线,但原理是相同的。

        // ### Part 1 ###
        var combinedLineParts = filteredLine.GroupBy(c => new { c.Fid, c.FidPart})
                .Select(g => new ElmLine()
        {
            Fid = g.Select(c => c.Fid).First(),
            FidPart = g.Select(c => c.FidPart).First(),
            CoordList = g.Select(c => new CoordList() 
                                          {XCord = c.CoordX, YCord = c.CoordY}).ToList()
                         .GroupBy(x => new {x.XCord, x.YCord})
                         .Select(x => x.First()).ToList(),
        }).ToList();
        // ### Part 2 ###
        foreach (var group in combinedLineParts.GroupBy(c => c.Fid))
        {
            List<List<CoordList>> coordList = group.Select(c => c.CoordList).ToList();
            if (coordList.Count > 2)
            {
                int[] startPoint = FindStartPoint(coordList);
                // if start point is not on top of the list, 
                // move it to the top (to: {0,0})
                if (startPoint[0] > 0 || startPoint[1] > 0)
                {
                    SwapElements(coordList, startPoint, new int[] { 0, 0 });
                }
                // Rearange List to sort the lineparts
                int groupNumb = 0;
                while (groupNumb < coordList.Count - 1)
                {
                    RearrangeList(coordList, groupNumb);
                    groupNumb++;
                }
            }
        // ### Part 3 ###
            // create a new list with the sorted lineparts
            combinedLines.Add( new ElmLine()
                {
                    Fid = group.Key,
                    CoordList = coordList.SelectMany(d => d)
                                         .Select(c => new {c.XCord, c.YCord})
                                         .Distinct()
                                         .Select(c => new CoordList() 
                                            {XCord = c.XCord, YCord = c.YCord}).ToList(), 
                });
        }
        return combinedLines;

FindStartPoint(), SwapElements(), RearrangeList()是我自己的方法,我解决我的具体排序问题。