Related
I get NullReferenceException when I want to add item to my elements in jagged array.
public List<int>[][] Map;
void Start()
{
Map = new List<int>[60][];
for(byte x = 0; x < 60 ; x++)
{
Map[x] = new List<int>[60];
// initialization of rows
}
Map [23] [34].Add (21);
}
You have a jagged array which each of its elements is a List<int>. You initialize the array but not the elements.
So when you call Add on an uninitialized element which is a List<int> you get the exception.
Map = new List<int>[60][];
for (int x = 0; x < 60; x++)
{
Map[x] = new List<int>[60];
for (int y = 0; y < 60; y++)
{
Map[x][y] = new List<int>(); // initializing elements
}
// initialization of rows
}
Map[23][34].Add(21);
I was just wondering, is there a better way to initialize c# multidimensional array of objects (reference type).
Here is my code:
Board = new Field[BoardHeight, BoardWidth];
for (int i = 0; i < BoardHeight; i++)
{
for (int j = 0; j < BoardWidth; j++)
{
Board[i, j] = new Field();
}
}
If I could get rid of for/for loops and replace it with single line? That'd be just great.
Nested for loops is generally most readable/accepted approach to initialize 2d array.
If you really need single statement - one option is to use Enumerable.Aggregate (to create/fill array in single statement) and Enumerable.Range + Enumerable.SelectMany (to create indexes using How do I take the Cartesian join of two lists in c#?):
Board = Enumerable.Range(0, BoardHeight)
.SelectMany(x => Enumerable.Range(0, BoardWidth), (row,col)=>new {row,col})
.Aggregate(new Field[BoardHeight, BoardWidth],
(board, position)=>
{
board[position.row,position.col] = new Field();
return board;
});
(Readability of this code is open for discussion)
A bit more practical solution:
for (var i = 0; i < BoardHeight* BoardWidth; i++)
{
Board[i / BoardWidth, i % BoardWidth] = new Field();
}
If you really need "single line initialization" - refactor nested for loop into method (possibly generic) and call it wherever you want with "single line". Something like:
public TField[,] InitializeArray<TField>(
int BoardHeight, int BoardWidth) where TField:new()
{
var Board = new TField[BoardHeight, BoardWidth];
for (int i = 0; i < BoardHeight; i++)
{
for (int j = 0; j < BoardWidth; j++)
{
Board[i, j] = new TField();
}
}
return Board;
}
More for curiosity's sake than anything else, I adapted some old code into an extension method that will initialize every member of an array of arbitrary rank with the results of a provided Func. You'd use it like this (with 2D, 3D, whatever-D arrays):
var board = new Foo[10, 20];
board.Fill(() => new Foo());
Here's the extension method:
static class Extensions
{
public static void Fill(this Array arr, Func<object> gen)
{
// Get the dimensions of the array
var dims = Enumerable.Range(0, arr.Rank)
.Select(arr.GetLength)
.ToArray();
Func<int, int, int> product = (i1, i2) => i1 * i2;
for (var i = 0; i < arr.Length; i++)
{
var indices = dims.Select((d, n) => (i/dims.Take(n).Aggregate(1, product))%d).ToArray();
arr.SetValue(gen(), indices);
}
}
}
Thanks to MarcinJuraszek and Alexei Levenkov suggestions I came out with this answer. Technically is not a single statement (all in all), but if you hide extension method ;-) it will look very clean:
Board = new Field[BoardHeight, BoardWidth];
Board.Init();
Hide this somewhere:
public static class MyExtensions
{
public static void Init<T>(this T[,] board) where T : new()
{
for (int i = 0; i < board.GetLength(0); i++)
{
for (int j = 0; j < board.GetLength(1); j++)
{
board[i,j] = new T();
}
}
}
}
I think this is the easiest one-liner (split to multiple lines for readability) to initialize your array:
var board = Enumerable.Range(0, BoardWidth)
.Select(row => Enumerable.Range(0, BoardHeight)
.Select(value =>new TField())
.ToArray())
.ToArray();
I have a string array defined in c# as
string[,] options = new string[100,3];
Throughout the code it gets populated with data but not always filled.
So if I have 80 parts of it filled and 20 parts of it not filled. The 20 parts have nulls in them or 60 nulls at the end. Is there an easy way to resize the array so that after filling it the array is the same as
String[,] options = new string[80,3];
It would have to be resized based on the position of the first set of 3 nulls it found.
If this was a jagged array I would have done
options = options.Where(x => x != null).ToArray();
The method is quite long, because it has to check every row twice...
public static string[,] RemoveEmptyRows(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we count the non-emtpy rows
int nonEmpty = 0;
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty++;
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty, length2];
for (int i1 = 0, i2 = 0; i2 < nonEmpty; i1++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i1, j] != null)
{
// If the i1 row is not empty, we copy it
for (int k = 0; k < length2; k++)
{
strs2[i2, k] = strs[i1, k];
}
i2++;
break;
}
}
}
return strs2;
}
Use it like:
string[,] options = new string[100, 3];
options[1, 0] = "Foo";
options[3, 1] = "Bar";
options[90, 2] = "fiz";
options = RemoveEmptyRows(options);
As suggested by Alexei, there is another way of doing this:
public static string[,] RemoveEmptyRows2(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we put somewhere a list of the indexes of the non-emtpy rows
var nonEmpty = new List<int>();
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty.Add(i);
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty.Count, length2];
// And we copy the rows from strs to strs2, using the nonEmpty
// list of indexes
for (int i1 = 0; i1 < nonEmpty.Count; i1++)
{
int i2 = nonEmpty[i1];
for (int j = 0; j < length2; j++)
{
strs2[i1, j] = strs[i2, j];
}
}
return strs2;
}
This one, in the tradeoff memory vs time, chooses time. It is probably faster, because it doesn't have to check every row twice, but it uses more memory, because it puts somewhere a list of the non-empty indexes.
I went for all rows until you find an row with all null values:
Needs some clean up and will obviously remove non-null rows that occur after the first all null row. The requirement wasn't too clear here
EDIT: Just seen the comment clarifying requirement to remove all null rows - I've tweaked the below to avoid downvotes but a more comprehensive answer is already accepted (and is more efficient) :)
void Main()
{
string[,] options = new string[100,3];
options[0,0] = "bleb";
options[1,1] = "bleb";
options[2,0] = "bleb";
options[2,1] = "bleb";
options[3,2] = "bleb";
options[4,1] = "bleb";
string[,] trimmed = TrimNullRows(options);
Console.WriteLine(trimmed);
}
public string[,] TrimNullRows(string[,] options)
{
IList<string[]> nonNullRows = new List<string[]>();
for (int x = 0; x < options.GetLength(0); x++)
{
bool allNull = true;
var row = new string[options.GetLength(1)];
for (int y = 0; y < options.GetLength(1); y++)
{
row[y] = options[x,y];
allNull &= options[x,y] == null;
}
if (!allNull)
{
nonNullRows.Add(row);
}
}
var optionsTrimmed = new string[nonNullRows.Count, options.GetLength(1)];
for (int i=0;i<nonNullRows.Count;i++)
{
for (int j=0;j<options.GetLength(1);j++)
{
optionsTrimmed[i, j] = nonNullRows[i][j];
}
}
return optionsTrimmed;
}
You can also get yourself some helpers to convert between jagged and multi-dimensional representations. This is pretty silly, of course, but for arrays as small as the ones you're showing (and also, very sparse arrays), it'll be fine.
void Main()
{
string[,] options = new string[100,3];
options[3, 1] = "Hi";
options[5, 0] = "Dan";
var results =
options
.JagIt()
.Where(i => i.Any(j => j != null))
.UnjagIt();
results.Dump();
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> JagIt<T>(this T[,] array)
{
for (var i = 0; i < array.GetLength(0); i++)
yield return GetRow(array, i);
}
public static IEnumerable<T> GetRow<T>(this T[,] array, int rowIndex)
{
for (var j = 0; j < array.GetLength(1); j++)
yield return array[rowIndex, j];
}
public static T[,] UnjagIt<T>(this IEnumerable<IEnumerable<T>> jagged)
{
var rows = jagged.Count();
if (rows == 0) return new T[0, 0];
var columns = jagged.Max(i => i.Count());
var array = new T[rows, columns];
var row = 0;
var column = 0;
foreach (var r in jagged)
{
column = 0;
foreach (var c in r)
{
array[row, column++] = c;
}
row++;
}
return array;
}
}
The JagIt method is pretty simple of course - we'll just iterate over the rows, and yield the individual items. This gives us an enumerable of enumerables, which we can use in LINQ quite easily. If desired, you could transform those into arrays, of course (say, Select(i => i.ToArray()).ToArray()).
The UnjagIt method is a bit more talkative, because we need to create the target array with the correct dimensions first. And there's no unyield instruction to simplify that :D
This is pretty inefficient, of course, but that isn't necessarily a problem. You could save yourself some of the iterations by keeping the inner enumerable an array, for example - that will save us having to iterate over all the inner items.
I'm mostly keeping this as the memory-cheap, CPU-intensive alternative to #xanatos' memory-intensive, CPU-cheap (relatively).
Of course, the main bonus is that it can be used to treat any multi-dimensional arrays as jagged arrays, and convert them back again. General solutions usually aren't the most efficient :D
Yet another variant with linq
static string[,] RemoveNotNullRow(string[,] o)
{
var rowLen = o.GetLength(1);
var notNullRowIndex = (from oo in o.Cast<string>().Select((x, idx) => new { idx, x })
group oo.x by oo.idx / rowLen into g
where g.Any(f => f != null)
select g.Key).ToArray();
var res = new string[notNullRowIndex.Length, rowLen];
for (int i = 0; i < notNullRowIndex.Length; i++)
{
Array.Copy(o, notNullRowIndex[i] * rowLen, res, i * rowLen, rowLen);
}
return res;
}
In a great many places in the software I write, there are three-dimensioned arrays of short or float, usually with several million elements. The data is best understood conceptually as a three-dimensioned array, since it describes values at regular locations in space.
I saw a mention elsewhere here that the .NET CLR is not terribly "performant" when it comes to traversing those arrays, for example, when computing new values and populating a second, equally sized and dimensioned array. If this is true, why is that so?
For reasons of readability I've not settled on the idea of using jagged arrays, yet, but if that's really the answer then I'm willing, but:
To get around this it's been proposed to me that I format the data as a single dimensioned array. For example, if my array has dimensions with magnitudes m, n, and o, then I would create a float[m*n*o] instead of a float[m,n,o], and write my own indexer to get to the correct array locations during traversal.
The specific use case is in parallelizing the traversal, such as:
Parallel.For(0,m)((x) => for(y=0,y<n,y++) { for(z=0,z<o,z++) doSomething(array(x,y,z)); });
Where in the single-indexed case there would be a Parallel.ForEach(myArray, (position) => doSomething(array(Position))) kind of thing going on instead of the nested for loops.
So, the question is, really, would that be any faster than relying on the CLR array indexing that's built in?
EDIT: I've supplied my own answer below, based on some timing tests. The code is included.
One huge thing to consider is the order of traversal. Memory caching is an important part of modern processor performance and cache misses can be (relatively) expensive. If you index the array across a 'long' dimension that results in crossing cache boundaries, you may cause frequent misses as part of indexing. As such, the order in which you index is important. This often means you want to take care in how you choose to order your indices.
Also, when copying, consider that multiple indexing requires computing the 'true' index to the underlying memory block using multiplication/addition. If you're just copying all elements, though, you could simply increment a single index and access each element without additional computation required.
There are also various condition checks that occur when accessing arrays by index (making the IndexOutOfRangeException possible), which requires more checks when you access via multiple indices. I believe (though I'm not entirely sure) that the jitter can sometimes optimize single dimensional array access using a simple loop by checking the range only once, rather than on every indexing operation.
I ran some timings and found that overall performance is that it makes little or no difference:
I used this code below. The timings I got were basically identical in each case:
public partial class Form1 : Form
{
int ArrayDim1 = 50;
int ArrayDim23 = 500;
int ParallelSplit = 50;
int DoSomethingSize = 100;
Double sqRoot = 0;
Single[, ,] multidim = null;
Single[] singleDim = null;
Single[][][] jagged = null;
ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 36 };
public Form1()
{
InitializeComponent();
multidim = new Single[ArrayDim1, ArrayDim23, ArrayDim23];
for (int x = 0; x < ArrayDim1; x++)
for (int y = 0; y < ArrayDim23; y++)
for (int z = 0; z < ArrayDim23; z++)
multidim[x, y, z] = 1;
singleDim = new Single[ArrayDim1 * ArrayDim23 * ArrayDim23];
for (int i = 0; i < singleDim.Length; i++)
singleDim[i] = 1;
jagged = new Single[ArrayDim1][][];
for (int i = 0; i < ArrayDim1; i++)
{
jagged[i] = new Single[ArrayDim23][];
for (int j = 0; j < ArrayDim23; j++)
{
jagged[i][j] = new Single[ArrayDim23];
}
}
}
private void btnGO_Click(object sender, EventArgs e)
{
int loopcount = 1;
DateTime startTime = DateTime.Now;
for (int i = 0; i < loopcount; i++)
{
TestMultiDimArray(multidim);
}
textBox1.Text = DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("#,###");
startTime = DateTime.Now;
for (int i = 0; i < loopcount; i++)
{
TestSingleArrayClean(singleDim);
}
textBox2.Text = DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("#,###");
startTime = DateTime.Now;
for (int i = 0; i < loopcount; i++)
{
TestJaggedArray(jagged);
}
textBox3.Text = DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("#,###");
}
public void TestJaggedArray(Single[][][] multi)
{
Parallel.For(0, ArrayDim1, po, x =>
{
for (int y = 0; y < ArrayDim23; y++)
{
for (int z = 0; z < ArrayDim23; z++)
{
DoComplex();
multi[x][y][z] = Convert.ToSingle(Math.Sqrt(123412341));
}
}
});
}
public void TestMultiDimArray(Single[, ,] multi)
{
Parallel.For(0, ArrayDim1, po, x =>
{
for (int y = 0; y < ArrayDim23; y++)
{
for (int z = 0; z < ArrayDim23; z++)
{
DoComplex();
multi[x, y, z] = Convert.ToSingle(Math.Sqrt(123412341));
}
}
});
}
public void TestSingleArrayClean(Single[] single)
{
Parallel.For(0, single.Length, po, y =>
{
//System.Diagnostics.Debug.Print(y.ToString());
DoComplex();
single[y] = Convert.ToSingle(Math.Sqrt(123412341));
});
}
public void DoComplex()
{
for (int i = 0; i < DoSomethingSize; i++)
{
sqRoot = Math.Log(101.101);
}
}
}
To assign specific value to 1D array I'm using LINQ like so:
int[] nums = new int[20];
nums = (from i in nums select 1).ToArray<int>();
nums[0] = 2;
There is similar way to do so in 2D ([x,y]) array?
Or short way, without using nested loops?
If you really want to avoid nested loops you can use just one loop:
int[,] nums = new int[x,y];
for (int i=0;i<x*y;i++) nums[i%x,i/x]=n;
You can make it easier by throwing it into some function in a utility class:
public static T[,] GetNew2DArray<T>(int x, int y, T initialValue)
{
T[,] nums = new T[x, y];
for (int i = 0; i < x * y; i++) nums[i % x, i / x] = initialValue;
return nums;
}
And use it like this:
int[,] nums = GetNew2DArray(5, 20, 1);
LINQ doesn't work particularly well with multi-dimensional arrays.
Jagged arrays aren't too bad:
var array = Enumerable.Range(0, 10)
.Select(x => Enumerable.Repeat('x', 10).ToArray())
.ToArray();
... but rectangular arrays don't have any specific support. Just use loops.
(Note the use of Enumerable.Repeat as a somewhat simpler approach to creating the 1-dimensional array, btw.)
Well, this might be cheating because it simply moves the looping code to an extension method, but it does allow you to initialize your 2D array to a single value simply, and in a fashion similar to how you can initialize a 1D array to a single value.
First, as Jon Skeet mentioned, you could clean up your example of initializing a 1D array like this:
int [] numbers = Enumerable.Repeat(1,20).ToArray();
With my extension method, you will be able to initialize a 2D array like this:
public static T[,] To2DArray<T>(this IEnumerable<T> items, int rows, int columns)
{
var matrix = new T[rows, columns];
int row = 0;
int column = 0;
foreach (T item in items)
{
matrix[row, column] = item;
++column;
if (column == columns)
{
++row;
column = 0;
}
}
return matrix;
}
One way you could do this is like so:
// Define a little function that just returns an IEnumerable with the given value
static IEnumerable<int> Fill(int value)
{
while (true) yield return value;
}
// Start with a 1 dimensional array and then for each element create a new array 10 long with the value of 2 in
var ar = new int[20].Select(a => Fill(2).Take(10).ToArray()).ToArray();
May I suggest a new extension method.
public static class TwoDArrayExtensions
{
public static void ClearTo(this int[,] a, int val)
{
for (int i=a.GetLowerBound(0); i <= a.GetUpperBound(0); i++)
{
for (int j=a.GetLowerBound(1); j <= a.GetUpperBound(1); j++)
{
a[i,j] = val;
}
}
}
}
Use it like this:
var nums = new int[10, 10];
nums.ClearTo(1);
You can create a simple method that loops over all elements and initializes them:
public static void Fill2DArray<T>(T[,] arr, T value)
{
int numRows = arr.GetLength(0);
int numCols = arr.GetLength(1);
for (int i = 0; i < numRows; ++i)
{
for (int j = 0; j < numCols; ++j)
{
arr[i, j] = value;
}
}
}
This uses the same syntax as Array.Fill and will work for an array of any type