简体   繁体   中英

Create a dictionary from the common property values of a List<Object>

Let's say we have a list of _2DLine objects.

public class _2DLine
{
    public double X1 { get; set; }
    public double Y1 { get; set; }
    public double X2 { get; set; }
    public double Y2 { get; set; }
}

var L1 = new _2DLine { X1 = 0, Y1 = 0, X2 = 100, Y2 = 100 };
var L2 = new _2DLine { X1 = 50, Y1 = 50, X2 = 200, Y2 = 200 };
var L3 = new _2DLine { X1 = 0, Y1 = 0, X2 = 200, Y2 = 200};
var L4 = new _2DLine { X1 = 100, Y1 = 100, X2 = 50, Y2 = 50};

var MyLines = new List<_2DLine>
{
    L1,
    L2,
    L3,
    L4
}

As you can see, some of the lines have points in common. How can we extract all the points from this list into a dictionary so that for a specific _2DPoint(X,Y) we will have a list of the lines entering or exiting that point.

var MyPoints = new Dictionary<_2DPoint, List<_2DLine>>();

public class _2DPoint
{
    public double X { get; set; }
    public double Y { get; set; }
}

The final result would be like this:

  Key              |    Value
-----------------------------------
_2DPoint(0,0)      |   { L1, L3 }
_2DPoint(100,100)  |   { L1, L4 }
_2DPoint(50,50)    |   { L2, L4 }
_2DPoint(200,200)  |   { L2, L3 }

Project lines to flattened sequence of points and lines, then group sequence by points (I use anonymous types, because they have implemented Equals and GetHashCode) and create dictionary:

var result = MyLines.SelectMany(l => new[] { 
                                   new { X = l.X1, Y = l.Y1 },
                                   new { X = l.X2, Y = l.Y2 }
                                }, (l,p) => new { Point = p, Line = l })
                   .GroupBy(x => x.Point)
                   .ToDictionary(g => new _2DPoint { X = g.Key.X, Y = g.Key.Y },
                                 g => g.Select(x => x.Line).ToList());

Suggestion - use your points as start and end of lines, instead having four coordinates. Also improve naming. Don't start class names from underscore, use camelCase names for local variables. Eg

public class Line
{
    public Line(double startX, double startY, double endX, double endY)
        : this(new Point(startX, startY), new Point(endX, endY))
    {
    }

    public Line(Point start, Point end)
    {
        Start = start;
        End = end;
    }
    public Point Start { get; private set; }
    public Point End { get; private set; }
}

I would also create point as value object and override Equals and GetHashCode methods to compare points by their values:

public class Point
{
    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
    public double X { get; private set; }
    public double Y { get; private set; }

    public override bool Equals(object obj)
    {
        Point other = obj as Point;
        if (other == null)
            return false;

        return X == other.X && Y == other.Y;
    }

    public override int GetHashCode()
    {            
        return X.GetHashCode() * 19 + Y.GetHashCode();
    }
}

Now creating list of lines looks like:

var lines = new List<Line> {
    new Line(0, 0, 100, 100),
    new Line(50, 50, 200, 200),
    new Line(0, 0, 200, 200),
    new Line(100, 100, 50, 50)
};

And points dictionary creation:

var points = lines.SelectMany(l => new[] { l.Start, l.End },
                              (l, p) => new { Line = l, Point = p })
                  .GroupBy(x => x.Point)
                  .ToDictionary(g => g.Key, g => g.Select(x => x.Line).ToList());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM