views:

169

answers:

3

I have an ICollection<MapNode>. Each MapNode has a Position attribute, which is a Point. I want to sort these points first by Y value, then by X value, and put them in a multidimensional array (MapNode[,]).

The collection would look something like this:

(30, 20)
(20, 20)
(20, 30)
(30, 10)
(30, 30)
(20, 10)

And the final product:

(20, 10)    (20, 20)    (20, 30)
(30, 10)    (30, 20)    (30, 30)

Here is the code I have come up with to do it. Is this hideously unreadable? I feel like it's more hacky than it needs to be.

    private Map createWorldPathNodes()
    {
        ICollection<MapNode> points = new HashSet<MapNode>();
        Rectangle worldBounds = WorldQueryUtils.WorldBounds();

        for (float x = worldBounds.Left; x < worldBounds.Right; x += PATH_NODE_CHUNK_SIZE)
        {
            for (float y = worldBounds.Y; y > worldBounds.Height; y -= PATH_NODE_CHUNK_SIZE)
            {
                // default is that everywhere is navigable;
                // a different function is responsible for determining the real value
                points.Add(new MapNode(true, new Point((int)x, (int)y))); 
            }
        }

        int distinctXValues = points.Select(node => node.Position.X).Distinct().Count();
        int distinctYValues = points.Select(node => node.Position.Y).Distinct().Count();

        IList<MapNode[]> mapNodeRowsToAdd = new List<MapNode[]>();

        while (points.Count > 0) // every iteration will take a row out of points
        {
            // get all the nodes with the greatest Y value currently in the collection
            int currentMaxY = points.Select(node => node.Position.Y).Max();
            ICollection<MapNode> ythRow = points.Where(node => node.Position.Y == currentMaxY).ToList();

            // remove these nodes from the pool we're picking from
            points = points.Where(node => ! ythRow.Contains(node)).ToList(); // ToList() is just so it is still a collection

            // put the nodes with max y value in the array, sorting by X value
            mapNodeRowsToAdd.Add(ythRow.OrderByDescending(node => node.Position.X).ToArray());
        }

        MapNode[,] mapNodes = new MapNode[distinctXValues, distinctYValues];

        int xValuesAdded = 0;
        int yValuesAdded = 0;
        foreach (MapNode[] mapNodeRow in mapNodeRowsToAdd)
        {
            xValuesAdded = 0;
            foreach (MapNode node in mapNodeRow)
            {
                // [y, x] may seem backwards, but mapNodes[y] == the yth row
                mapNodes[yValuesAdded, xValuesAdded] = node; 
                xValuesAdded++;
            }
            yValuesAdded++;
        }

        return pathNodes;
    }

The above function seems to work pretty well, but it hasn't been subjected to bulletproof testing yet.

+1  A: 
  1. You may want to consider using a jagged array instead of a multidimensional array, unless you know for sure that your results are going to fit into a pretty matrix.

  2. Split the code, it is not very readable.

I'd sugget you break it this way:

  1. Get a distinct sorted collection of Y values (using LINQ).
  2. For each Y-value, gather all points with that y-value, sort them by their x-values, and add them to your resulting collection (again, using LINQ).

If you are able to read VB code, i'll attach a sample code. I think it would come out simpler and clearer than your current method.

M.A. Hanin
I can read VB a bit, if you're already got it written.
Rosarch
A: 
 int[,] source = { {30, 20}, {20, 20}, {20, 30}, {30, 10}, {30, 30}, {20, 10} } ;

 List<KeyValuePair<int, int>> listSource = new List<KeyValuePair<int,int>>();

 for( int i=0; i < source.GetLength(0); i++)
    listSource.Add(new KeyValuePair<int,int>(source[i,0], source[i,1]));

 var result = from l in listSource
              group l by l.Key into grp
              from g in grp
              orderby g.Key, g.Value
              select new
              {
                g.Key,
                g
              };

 int key = 0;
 foreach( var r in result )
 {
    if( key != r.Key )
    {
       Console.WriteLine();
       key = r.Key;
    }
    Console.Write( "{0}, {1} | ", r.g.Key, r.g.Value );
 }
Partha Choudhury
A: 
public class MapNode
{
    public double X { get; set; }
    public double Y { get; set; }

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

public static class Sorter
{
    public static void Sort(ICollection<MapNode> nodes)
    {
        IEnumerable<MapNode> sortedX = nodes.OrderBy(mapnode => mapnode.X);
        IEnumerable<MapNode> sortedY = sortedX.OrderBy(mapnode => mapnode.Y);
        Array.ForEach(sortedY.ToArray(), mn => Console.WriteLine("{0}, {1}", mn.X, mn.Y));
    }
}

Breaking up SortedY into a two-dimensional array is the tricky part.

Repo Man