简体   繁体   中英

Most elegant way to get the array element by position?

I have an array:

private int[,] _blocks = new int[6, 4];

It represents a set of blocks which is 6 deep horizontally and 4 deep vertically. Graphically it looks like this:

alt text http://www.angryhacker.com/toys/array.png

I need a function that would take in a number, from 1 to 24 and return the array element that matches. So for number 14, I'd get back _blocks[1, 2];

I've created a simplistic function:

private int GetBlockByPosition(int position)
{
    int count = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (++count == position)
                return _blocks[i, j];
        }
    }
    return -1;
}    

But this seems very wasteful and smells bad. Is there a more elegant and faster way?

Both in the horizontal direction and the vertical direction, you can see a pattern in your table of numbers. You can determine the horizontal position with position / 6 and a vertical position with position % 6 -- the modulo operation.

private int GetBlockByPosition(int position)
{
    return _blocks[((position + 6) / 6) - 1, position % 6];
}

This makes mathematical sense. Division increases in chunks, and modulo (remainder on division) increases one by one. The math is pretty simple.

I'm not sure I follow, but why can't you just calculate he indexes based on the position? Something like this:

return _blocks[((position - 1) % 6),((position + 5) / 6) - 1];

I think you can do like this :

private int GetBlockByPosition(int position)
{
    return _blocks[(position - 1 ) % 6 , (position - 1) / 6];
}

Are the numbers in your array ACTUALLY 1, 2, 3, ... or are you just using them as an example?

If there isn't any pattern to the data in your array that can be taken advantage of, then it looks like the simplistic option may be your best bet.

Or you could always make a one-time pass of the entire structure and build a hash table to be used in subsequent calls...

Depending on your definition of elegant, the following is perhaps a more functional way of solving the problem:

class Program
{
    static void Main(string[] args)
    {
        var blocks = new int[,] {{1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18},{19,20,21,22,23,24}};
        var position = blocks.FirstPositionOf(14);
        Console.WriteLine(position.X + "," + position.Y + " has the element " + blocks[position.X,position.Y]);
    }

}

class PositionTuple
{
    public int X {get; set;}
    public int Y {get; set;}
}

static class ArrayExtensions
{
    public static IEnumerable<int> AsEnumerable(this int[,] someTwoDimensionalArray)
    {
        foreach (var num in someTwoDimensionalArray)
            yield return num;
    }

    public static PositionTuple FirstPositionOf(this int[,] someTwoDimensionalArray, int someNumber)
    {
        return someTwoDimensionalArray
            .AsEnumerable()
            .Select((num, index) => new { Number = num, Tuple = new PositionTuple { X = index / (someTwoDimensionalArray.GetUpperBound(1) + 1), Y = index % (someTwoDimensionalArray.GetUpperBound(1)+1) }})
            .Where(pair => pair.Number == someNumber)
            .Select(pair => pair.Tuple)
            .First();
    }
}

I would make a more flexible function that can be used elsewhere if you need to

public static T Get2DArrayValueByPosition<T> (T[,] arr, int position)
{
    // Gets the size of the array in first dimention
    step  = arr.GetUpperBound(0) + 1;

    return arr[(position / step ), position % step];
}

Comprehensive solution considering the corner cases:

private int GetBlockByPosition(int position)
{
    if(position % 6 == 0) { // last cells in each row. 6 gives [0,5]
        return _blocks[(position / 6) - 1, (position - 1) % 6];
    } else { // 11 gives [1,4]
        return _blocks[position / 6 , (position % 6) - 1];
    }
}
int result = GetByPosition(_blocks, 14);

private int GetByPosition(int[,] array, int position)
{
    return GetByPositionBaseZero(array, position - 1);
}

private int GetByPositionBaseZero(int[,] array, int position)
{
    int width = array.GetLength(0);
    return array[position % width, position / width];
}

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