65

I do not want to use a jagged array and I have a 2D array and I want to get a complete column or row without looping through it. Does anyone have an idea how it can be done.

// 1   2   3
// 4   5   6
double [,]  array = new double [2,3];

Out: 1   2   3  or 2   5 
eduherminio
  • 1,514
  • 1
  • 15
  • 31
User1551892
  • 3,236
  • 8
  • 32
  • 52
  • https://stackoverflow.com/a/51241629/1447389 should be the accepted answer. – Soleil Dec 25 '19 at 21:21
  • No it should not. Though a very nice clean bit of code, it does not actually answer the question re: not looping which linq naturally does under the hood. – Paul Childs Nov 15 '21 at 23:24

8 Answers8

107

To get a specific row or column from the multidimensional array you can use some LINQ:

public class CustomArray<T>
{
    public T[] GetColumn(T[,] matrix, int columnNumber)
    {
        return Enumerable.Range(0, matrix.GetLength(0))
                .Select(x => matrix[x, columnNumber])
                .ToArray();
    }

    public T[] GetRow(T[,] matrix, int rowNumber)
    {
        return Enumerable.Range(0, matrix.GetLength(1))
                .Select(x => matrix[rowNumber, x])
                .ToArray();
    }
}
Alex Podles
  • 1,109
  • 2
  • 7
  • 4
  • 1
    Unlike the accepted answer, this works for `bool` as well as non-primitive types – BCA Oct 29 '18 at 20:06
  • 2
    Conceptually havent you got this mixed up? Shouldnt the code have: matrix[columnNumber, x] and matrix[x, rowNumber]? Most people conceptually would think of a one dimension array being a single "row" and multiple columns and a two dimension array having multiple rows. After all number of columns is the X axis and rows the Y axis. – bytedev Nov 05 '19 at 02:17
  • 4
    This should be the accepted answer - it is generic, elegant, and allows for queries that don't require enumerating the entire result list. – MandisaW Dec 02 '19 at 02:49
  • @bytedev while you are correct in one sense and the idea of row, col is much easier to grasp. Multidimensional arrays have no geometric definition. It depends how you program arrays. If anyone still wants to fix this code, swap the function and variable names. – Osama Shabrez Feb 12 '20 at 10:47
  • @bytedev Yeah it is confusing that it's the other way round from geometric points (columns are like the _x_-axis but are indexed second). However this is probably a question for the wider computer science community. – Ruben9922 Mar 29 '20 at 14:08
  • @bytedev I think it's because matrices in maths are indexed as _[row, column]_ so it makes sense to keep it consistent with that. Also, for a jagged array (array of arrays), often used in other languages to emulate 2D arrays, the outer array (indexed first) is generally thought of as containing the rows and each inner array (indexed second) is thought of as a single row (probably because that corresponds to how they're defined in code - might have one line of code to define each row), if that makes sense. – Ruben9922 Mar 29 '20 at 14:12
  • 7
    _whispered: use extension methods, not a new class._ – ruffin Jul 22 '20 at 17:25
  • What kind of performance does this have compared to a block copy, for rows? – Robinson Jun 21 '21 at 00:13
  • Copy row performance of the Block.Copy() will be better comparing to suggested solution. But you should keep in mind, that this methods (as well as Array.Copy()) are more generic and allows to copy non primitive types. Copy column performance should be almost the same, as there is the big chance of cache miss in both – Alex Podles Mar 13 '22 at 17:35
24

You can optimise it for getting rows by using Buffer.BlockCopy(), but to get a column you'll have to loop. Buffer.BlockCopy() ultimately uses a processor instruction to copy a block of memory, so it is pretty fast.

It's convenient to put the code into an extension method to make it easier to call. Note that Buffer.BlockCopy() can only be used on arrays of primitive types, i.e. int, double, char etc. This does NOT include string.

Here's a compilable example:

using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace ConsoleApplication4
{
    public static class Program
    {
        private static void Main()
        {
            var array = new [,]
            {
                {0.1, 0.2, 0.3, 0.4, 0.5},
                {1.1, 1.2, 1.3, 1.4, 1.5},
                {2.1, 2.2, 2.3, 2.4, 2.5},
                {3.1, 3.2, 3.3, 3.4, 3.5},
            };

            var row = array.GetRow(2);

            // This prints 2.1, 2.2, 2.3, 2.4, 2.5

            Console.WriteLine(string.Join(", ", row.Select(element => element.ToString())));
        }
    }

    public static class ArrayExt
    {
        public static T[] GetRow<T>(this T[,] array, int row)
        {
            if (!typeof(T).IsPrimitive)
                throw new InvalidOperationException("Not supported for managed types.");

            if (array == null)
                throw new ArgumentNullException("array");

            int cols = array.GetUpperBound(1) + 1;
            T[] result = new T[cols];

            int size;

            if (typeof(T) == typeof(bool))
                size = 1;
            else if (typeof(T) == typeof(char))
                size = 2;
            else
                size = Marshal.SizeOf<T>();

            Buffer.BlockCopy(array, row*cols*size, result, 0, cols*size);

            return result;
        }
   }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 1
    Note that this does not work with the primitive type `bool` – BCA Oct 29 '18 at 20:04
  • 2
    @BCA True, because Marshal.SizeOf(bool) is 4, not 1. I've fixed this by checking for that type explicitly and using a size of 1 if its a bool. It also failed for `char`, so I've put in a fix for that too. – Matthew Watson Nov 27 '18 at 13:13
8

As of March 2021, you can now use the very cool Span2D class for this!

If you are happy using spans (I highly recommend reading about them, they are awesome), you can use the following code

var span2D = new Span2D<double>(array);

//Gets the first row and returns a span of it
var rowSpan = span2D.GetRowSpan(0);

foreach(var number in rowSpan)
{
    //Do something with numbers
}

//Gets the 2nd Column as a RefEnumerable and converts it to an array
var column = span2D.GetColumn(1).ToArray();
Alex23
  • 401
  • 4
  • 8
  • 1
    See [here](https://learn.microsoft.com/en-us/dotnet/api/microsoft.toolkit.highperformance.span2d-1?view=win-comm-toolkit-dotnet-7.0) for docs of nuget pkg. Though, NB, it has been deprecated in favour of [CommunityToolkit.HighPerformance](https://www.nuget.org/packages/CommunityToolkit.HighPerformance/) – Andrew Matthews Feb 07 '23 at 03:50
0

an alternative way you can do it is by using a List instead of an array.

Specifically in your case you'd do something like that:

  1. Initially create an inner class that represents a tuple of the array
  2. Create a List of the inner class
  3. Populate the inner class
  4. Get the row that contains something specific
  5. Get the column that contains something specific
public static void Main(string[] args)
{
    // #2 -- Instantiate List of myClass
    List<myClass> myList = new List<myClass>();
    //
    // #3 -- Populate the list
    myList.Add(new myClass(1,2,3));           
    myList.Add(new myClass(3,4,5));
    myList.Add(new myClass(5,6,6));
    //
    // #4 -- Get the line where a == 1
    myList.Find(x=>x.a == 1);
    //
    // #5 -- Get column b
    myList.Select(x=>x.b);
}
// #1 -- Create the inner class
public class myClass
{
    public int a;
    public int b;
    public int c;
    public myClass(int a, int b, int c)
    {
        this.a =a;
        this.b =b;
        this.c =c;
    }
}
Pontios
  • 2,377
  • 27
  • 32
0

what is needed is a jagged array (not a multidimensional array)

https://msdn.microsoft.com/en-us/library/2s05feca.aspx

int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[5];
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };

full example with columns:

using System;
using System.Collections.Generic;

namespace Rextester
{
    public class Program
    {

        public static T[] column<T>(T[][] jaggedArray,int wanted_column)
        {
            T[] columnArray = new T[jaggedArray.Length];
            T[] rowArray;
            for(int i=0;i<jaggedArray.Length;i++)
            {
                rowArray=jaggedArray[i];
                if(wanted_column<rowArray.Length)
                    columnArray[i]=rowArray[wanted_column];
            }
            return columnArray;
        }

        public static void Main(string[] args)
        {
            //Your code goes here
                int[][] jaggedArray = new int[3][];
                jaggedArray[0] = new int[5];
                jaggedArray[1] = new int[] { 0, 2, 4, 6 };
                jaggedArray[2] = new int[] { 11, 22 };

                Console.WriteLine("Hello, world!");
                Console.WriteLine(string.Join(" ",jaggedArray[1]));
                Console.WriteLine(string.Join(" ",column(jaggedArray,1)));
        }
    }
}

similar idea, using extensions:

using System;
using System.Collections.Generic;

namespace Rextester
{
    public static class MyExtensions
    {
        public static string Extend(this Array array)
        {
            return "Yes, you can extend an array";
        }

        public static T[] column<T>(this T[,] multidimArray,int wanted_column)
        {
            int l=multidimArray.GetLength(0);
            T[] columnArray = new T[l];
            for(int i=0;i<l;i++)
            {
              columnArray[i]=multidimArray[i,wanted_column];
            }
            return columnArray;
        }

        public static T[] row<T>(this T[,] multidimArray,int wanted_row)
        {
            int l=multidimArray.GetLength(1);
            T[] rowArray = new T[l];
            for(int i=0;i<l;i++)
            {
              rowArray[i]=multidimArray[wanted_row,i];
            }
            return rowArray;
        }


    } 

    public class Program
    {


        public static void Main(string[] args)
        {
                Console.WriteLine("Hello, world!");

                int [,] multidimArray = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
                Console.WriteLine(string.Join(" ",multidimArray.column(0)));
                Console.WriteLine(string.Join(" ",multidimArray.row(0)));

        }
    }
}
pogosama
  • 1,818
  • 1
  • 24
  • 29
Shimon Doodkin
  • 4,310
  • 34
  • 37
  • 1
    That doesn't help with getting complete columns, just help with rows. – David Burg Aug 06 '18 at 06:17
  • there are no miracles. as Behrooz said here... you can fill this twice once for rows and once for columns. or use some accessor Behrooz said, or a function that uses an accessor and copies it to an array and returns an array. – Shimon Doodkin Aug 06 '18 at 15:00
  • 1
    The concept of a column makes no sense whatsoever for a jagged array. – Paul Childs Nov 15 '21 at 23:17
0

Here is how i have done it you can use

GetLength(0)

to get the columns and use

GetLength(1)

to get the rows of the 2 Dimensional array and you loop thru it with the for loop if any one else needs this.

string text = "";
for (int i = 0; i < array.GetLength(0); i++)
{
   text += Convert.ToString(array[i, 2]) + "\n";
}
E.Lahu
  • 399
  • 3
  • 15
  • 3
    In C# you should use a StringBuilder instead of a string if you're going to append it in a loop like this – Thymine Dec 11 '19 at 21:41
-1

My use case differs from the question, but is similar. I needed a 2D array of float[2] arrays, that I was using to represent complex numbers.

float[,,] myarray = new float[100,100,2];
float[] result = myarray[1,1];   <-- fails to compile needs all 3 coordinates

The jagged array Simmon mentioned provided the solution.

float[,][] myarray = new float[100,100][];
...
myarray[x,y] = new float[2];  <-- Initialise all elements of jagged 2D array in loop
...
float[] result = [100,100];
andrew pate
  • 3,833
  • 36
  • 28
-6

if you know the index of the numbers to output..then you don't need to use a loop to get the output desired...

double[,] array = new double[3,3] {{1,2,3}, {4,5,6}, {7,8,9}}; 

int firstNum = array[0,1];
int secondNum = array[1,1];

this will get 2, 5

SuncoastOwner
  • 263
  • 2
  • 9
  • 2
    The example in the question is simplified with a tiny array. The spirit of the question is to do this for arrays of arbitrary dimensions. Listing every command that would be in a loop is not answering the question of how to achieve the goal without a loop. – David Burg Aug 06 '18 at 06:25