Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find, in a list, all arrays whose first element is the same as the ones that match a condition

This is a question that was very hard to title.

In C#, I have a set of int[,] arrays stored in a List<int[,]> paths, each holding a set of coordenates to form a path. An example would be { {0,0}, {0,1}, {1,1}, {2,1} }.

Now, I want to keep in paths all paths that have the same first index as one of the first indexes of all paths that match a condition.

To better demonstrate what I mean, say I get all the paths that are of an odd length:

paths.Where(x => x.GetLength(0) % 2 == 1).ToList();

Say this returns a list containing some arrays whose first coords are either {0,0} or {0,1}, for example. I want paths to be paths.Where( x=> x's first coords are either {0,0} or {0,1}). How can i achieve this?

Hopefully, what I mean is understandable.

like image 706
cabralpinto Avatar asked Nov 07 '22 14:11

cabralpinto


1 Answers

If you're set on using your current data structure, then you can do this, but the syntax isn't going to be pretty. It's basically like what A. Milto suggested in his answer, except that you need bounds checking to avoid throwing an exception in the case of an empty path. So if you define your paths like so:

var arrayPaths = new List<int[,]>();
arrayPaths.Add(new[,] { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } });  // Include: starts with (0, 0)
arrayPaths.Add(new[,] { { 0, 1 }, { 0, 1 }, { 1, 1 }, { 2, 1 } });  // Include: starts with (0, 1)
arrayPaths.Add(new[,] { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } });  // Exclude: starts with (1, 0)
arrayPaths.Add(new int[0,0]);                                       // Exclude: has no data

Then the subset of paths that start at (0, 0) or (0, 1) is:

arrayPaths.Where(p => 
    p.GetUpperBound(0) >= 0 && 
    p.GetUpperBound(1) >= 1 && 
    (
        (p[0, 0] == 0 && p[0, 1] == 0) || 
        (p[0, 0] == 0 && p[0, 1] == 1)
    ));

Neville Nazerane had a good suggestion in his comment: using a data structure other than an array of integers to represent a point should result in code that's much more readily understandable. For instance, suppose you define a coordinate like so:

public struct Coordinate
{
    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }
    public int Y { get; }

    public bool Equals(int x, int y) => 
        X == x && Y == y;
}

Then you can define the set of paths given above like this:

var objectPaths = new List<List<Coordinate>>();
objectPaths.Add(new List<Coordinate> { new Coordinate(0, 0), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate> { new Coordinate(0, 1), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate> { new Coordinate(1, 0), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate>());

And now the subset of paths you're interested in is:

objectPaths.Where(p => p.Count > 0 && (p[0].Equals(0, 0) || p[0].Equals(0, 1)));

If you want a more concise syntax for specifying paths in code, then you might consider a very simple class to represent a path. For instance:

public class Path : List<Coordinate>
{
    public Path() { }

    public Path(params (int x, int y)[] coordinates) =>
        AddRange(coordinates.Select(c => new Coordinate(c.x, c.y)));
}

Now you can define the set of paths as:

var paths = new List<Path>();
paths.Add(new Path((0, 0), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path((0, 1), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path((1, 0), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path());

And the syntax for selecting the subset you wanted is the same as before.

like image 192
Joe Farrell Avatar answered Nov 14 '22 22:11

Joe Farrell