This question already has answers here:
An integer array as a key for Dictionary
(3 answers)
Closed 7 years ago.
I have the following piece of code
public Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x=0; x <= 10; x++) {
for (int y=0; y <= 10; y++) {
int[] cords = new int[]{x,y};
worldMap.Add(cords, "empty");
}
}
How do I get values from this dictionary?
Create an IEqualityComparer and define your dictionary using that. you can find sample code in below SO question:
An integer array as a key for Dictionary
You can iterate over the entire dictionary.
foreach (var map in worldMap)
{
Console.WriteLine(String.Join(", ", map.Key) + " - " + map.Value);
}
Or look it up by reference
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, "empty");
Console.WriteLine(worldMap[cords]);
}
}
When you use an array or class, the dictionary uses the reference to the object in the background. This makes some things impossible. For instance if you new another list with the same values, the dictionary will throw an exception saying the key is not found.
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, "empty");
//This will cause an exception
Console.WriteLine(worldMap[new int[] { x, y }]);
}
}
Consider using System.Drawing.Point instead of an int array. In general, Dictionaries will only match if it's the exact same object that went into it. Even another object with the same value won't work.
But some data types such as Point are different, and implement all the hashing and equality check features necessary to be used a keys in a dictionary.
After you populate the worldMap you can get your keys like this (You have to add using System.Linq):
List<int[]> coords = worldMap.Keys.ToList();
Then you can get any value from the coords List like this (Make sure you use a value within the bounds of your List):
worldMap[coords[0]]
Code Sample:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, String.Format("empty{0}_{1}", x, y));
}
}
// This should have 121 coordinates from (0, 0) to (10, 10)
List<int[]> coords = worldMap.Keys.ToList();
Console.WriteLine(worldMap[coords[21]]);
}
}
Demo
The use of an int[] for your word map coordinates is a poor choice for a number of reasons, not least of which I could send through zero, one, three, or more values when it seems like you specifically are using only two.
You would be far better off creating you own read-only class to hold your coordinates instead.
Try this class:
public sealed class WorldMapCoord : IEquatable<WorldMapCoord>
{
private readonly int _X;
private readonly int _Y;
public int X { get { return _X; } }
public int Y { get { return _Y; } }
public WorldMapCoord(int X, int Y)
{
_X = X;
_Y = Y;
}
public override bool Equals(object obj)
{
if (obj is WorldMapCoord)
return Equals((WorldMapCoord)obj);
return false;
}
public bool Equals(WorldMapCoord obj)
{
if (obj == null) return false;
if (!EqualityComparer<int>.Default.Equals(_X, obj._X)) return false;
if (!EqualityComparer<int>.Default.Equals(_Y, obj._Y)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int>.Default.GetHashCode(_X);
hash ^= EqualityComparer<int>.Default.GetHashCode(_Y);
return hash;
}
public override string ToString()
{
return String.Format("{{ X = {0}, Y = {1} }}", _X, _Y);
}
}
Now your code would look like this:
public Dictionary<WorldMapCoord, string> worldMap = new Dictionary<WorldMapCoord, string>();
for (int x=0; x <= 10; x++)
{
for (int y=0; y <= 10; y++)
{
worldMap.Add(new WorldMapCoord(x, y), "empty");
}
}
Since the class is read-only, and it implements GetHashCode & Equals you can use this code to retrieve values from the dictionary:
for (int x=0; x <= 10; x++)
{
for (int y=0; y <= 10; y++)
{
string value = worldMap[new WorldMapCoord(x, y)];
}
}
Related
I have a class that wraps 2d float[][] array into 1d float[] array:
class Wrapper
{
int CountX;
int CountY;
float[] Values;
}
for example something like this
{1, 2, 3, 4}
{5, 6, 7, 8}
would be wrapped into
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
And I want to find the fastest way to get its row or column.
Currently I'm using these methods
class Wrapper
{
int CountX;
int CountY;
float[] Values;
public float[] GetRow(int row)
{
var res = new float[CountX];
for(int i = 0; i < CountX; i++)
{
res[i] = Values[CountX*row + i];
}
return res;
}
public float[] GetColumn(int column)
{
var res = new float[CountY];
for (int i = 0; i < CountY; i++)
{
res[i] = Values[column + CountX*i];
}
return res;
}
}
With this usage:
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
//{1, 2, 3, 4}
//{5, 6, 7, 8}
wr.GetRow(1) // 5 6 7 8
wr.GetColumn(3) // 4 8
And what I am trying to accomplish is increasing performance. I'm pretty sure there is a way to do it faster using unsafe code, but I don't really know how to use pointers in C#
The fastest way to do this would usually be to not allocate or copy anything. Switching to unsafe is not going to help much with the real cost here, which is the allocation and copy; at best you can avoid some bounds checks.
Assuming you keep a 1D backing array, on the minor axis (by which I mean: contiguous data), it should be trivially possible to get a Span<float> of the relevant chunk of data: nothing more than that i.e. new ReadOnlySpan<float>(Values, CountX*row, CountX); on the major axis, maybe return something that is simply a flyweight readonly struct with an indexer into the data?
However, honestly I wonder if you should just use a float[,] and use regular x/y indexing.
Example; note that choosing which dimension to use as the inner one is important, as the direct Span<T> access will be faster than the indirect Row<T> access:
using System.Runtime.InteropServices;
var obj = new ArrayWrapper<float>(2, 3);
obj[1, 2] = 4;
Write(obj);
var row = obj.GetColumn(1);
for (int i = 0; i < row.Length; i++)
row[i] = i;
Write(obj);
var col = obj.GetRow(1);
for (int i = 0; i < col.Length; i++)
col[i] = i + 10;
Write(obj);
col = obj.GetRow(2);
for (int i = 0; i < col.Length; i++)
col[i] = i + 20;
Write(obj);
static void Write(ArrayWrapper<float> arr)
{
for (int y = 0; y < arr.Height; y++)
{
for (int x = 0; x < arr.Width; x++)
{
Console.Write(arr[x, y]);
Console.Write('\t');
}
Console.WriteLine();
}
Console.WriteLine();
}
readonly struct ArrayWrapper<T>
{
private readonly T[,] _array;
public int Width => _array.GetLength(0);
public int Height => _array.GetLength(1);
public ArrayWrapper(int width, int height) => _array = new T[width, height];
public ref T this[int x, int y] => ref _array[x, y];
public readonly Span<T> GetColumn(int x)
=> MemoryMarshal.CreateSpan(ref _array[x, 0], Height);
public readonly Row<T> GetRow(int y) => new(_array, y);
}
readonly struct Row<T>
{
private readonly T[,] _array;
private readonly int _y;
public Row(T[,] array, int y)
{
_array = array;
_y = y;
}
public bool IsEmpty => Length == 0;
public int Length => _array.GetLength(0); // Width
public ref T this[int x] => ref _array[x, _y];
}
For rows you should return a ReadonlySpan<T> since that would be a zero-copy operation. This is assuming your storage is row-major.
For columns you will need to copy elements. It can be useful to take the destination as a parameter. That way it might be possible to avoid repeated allocations. You can also update the index directly in the loop, I would expect that to help a little bit, but I have not done any profiling.
public void CopyColumn(int column, Span<float> res)
{
for (int i = column; i < Values.Length; i += CountX)
{
res[i] = Values[i];
}
}
If you want an array as a result you can add a helper method:
public float[] GetColumn(int column){
var res = new float[CountY];
CopyColumn(column, res);
return res;
}
I'm trying to figure out how to start a two dimensional array of an object.
I have point class and I want to create Rectangle class that build from 2D arrays of points.
I want to initialize the public Point[,] StartPoint somting like this = new [,]Point();
What the right way to do it?
{
class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point()
{
X = 0;
Y = 0;
}
public Point(int x , int y)
{
X = x;
Y = y;
}
}
}
Rectangele class :
{
class SRectangle
{
public Point[,] StartPoint;
public int Rows ;
public int Cols ;
public SRectangle(Point start , int row, int col)
{
Rows = row;
Cols = col;
for (int i = 0; i <= Rows; i++)
{
for (int j = 0; j <= Cols; j++)
{
StartPoint[i, j] = new Point(Rows + i, Cols + j);
}
}
}
}
}
You don't need a multidimensional array to keep a store of points, they themselves store x and y. You only need a simple array f points for a rectangle, But I see your concern, what you seem to want is:
but because it is class I can't write something like this 'public Point[,] StartPoint = new Point,;
You want to access points using an index as if you were storing X and Y combinations. But You aren't, you're storing an array of objects which have information about X and Y's however there is a way to get the point by (x,y)index
Insist on using a 2d array, in which Points[x,y] will have an object with the same X and Y i.e. you might as well have stored int[,] and it wouldn't have mattered.
Treat the array as a 2d but really having a 1d array.
//my rectangle class
class Rectangle
{
public Point[,] Points;
public int Rows ;
public int Cols ;
public Rectangle(Point start, int row, int col)
{
Rows = row;
Cols = col;
Points = new Point[Rows, Cols];
for (int i = 0; i <= Rows; i++)
{
for (int j = 0; j <= Cols; j++)
{
Points[i, j] = new Point(Rows + i, Cols + j);
}
}
//now use it like Points[x, y]
}
}
//my rectangle class
class Rectangle
{
public Point[] Points;
public int Rows;
public int Cols;
public Rectangle(Point start, int row, int col)
{
Rows = row;
Cols = col;
Points = new Point[Rows * Cols];
for (int i = 0; i <= Rows; i++)
{
for (int j = 0; j <= Cols; j++)
{
Points[(i * Cols) + j] = new Point(Rows + i, Cols + j);
}
}
}
//return the point if it exists else null
public Point? GetPoint(int x, int y)
{
int index = (i * Cols) + j;
if (index < Points.Length)
return Points[index];
return null;
}
}
I need to make a Jacobi Gauss program but I'm struggling with the start of it. We need to create a class which encapsulates a nxn matrix and stores it in a 2D array, with the default value being 3x3 with all values 0. We then have to add features to this and the rest of the Jacobi Gauss model. I've tried a few ways to make a matrix but I can't get one that relies on user input for size or defaults to a set size/values.
There are many ways to represent 2d matrix but the most simple is just as a native 2d array. By default a new array is filled with default values which for ints is zero. You can initialize yourself. Or, of course you can simply set the values after you create it.
Note in the first case I've created the array based off a integer. In this case for simplicity 1 value, obviously it does not have to be square.
static void Main(string[] _)
{
int dimensions = 3;
int[,] matrix = new int[dimensions, dimensions];
foreach (var v in matrix)
Console.WriteLine(v);
int[,] matrix2 = new int[,] { { 1,1,1},{ 2,2,2},{ 3,3,3} };
for (int x = 0; x < dimensions; ++x)
{
for (int y = 0; y < dimensions; ++y)
{
// matrix2[x,y] = whatever...
Console.WriteLine(matrix2[x, y]);
}
}
}
One option to wrap this in a class is to use a indexer method. For a simple square matrix with absolutely no error handling that might look like this:
public class MyMatrix
{
int[,] data = null;
public MyMatrix(int dims = 3)
{
this.data = new int[dims, dims];
}
public void SetDimensions(int dims)
{
if (this.data.GetLowerBound(0) != dims)
{
this.data = new int[dims, dims];
for (int x = 0; x < dims; ++x)
for (int y = 0; y < dims; ++y)
this.data[x, y] = 0;
}
}
public int this[int x, int y]
{
get { return this.data[x, y]; }
set { this.data[x, y] = value; }
}
public int Size => this.data.GetLowerBound(0);
}
static void Main(string[] _)
{
MyMatrix m = new MyMatrix(3);
for (int x = 0; x < m.Size; ++x)
{
for (int y = 0; y < m.Size; ++y)
{
// m[x,y] = whatever...
Console.WriteLine(m[x, y]);
}
}
}
I need to (initially) copy a C# 3D jagged array, foos, to another 3D array (and eventually add x, y, z dimensions). I thought I could use the same syntax/logic to copy foos as was used to build foos as I've done below (where row = 2, col = 3, z = 4):
private static void copyFooArray(Foo[][][] foos, ref Foo[][][] newArray)
{
for (int row = 0; row < foos.Length; row++)
{
newArray[row] = new Foo[foos[row].Length][];
for (int col = 0; col < foos[row].Length; col++)
{
newArray[row][col] = new Foo[foos[row][col].Length];
for (int z= 0; z< foos[row][col].Length; z++)
{
newArray[row][col][z] = new Foo();
newArray[row][col][z].member = foos[row][col][z].member;
}
}
}
Console.Read();
}
but I'm getting Index was outside the bounds of the array. on this line:
newArray[row] = new Foo[foos[row].Length][];
Why?
Foo Class:
public class Foo
{
public string member;
}
Thanks.
It doesn't look like your referenced array is being initialized properly. In order to set the value, your newArray must be initialized as the same size of your original.
For this to work you'd need to pass in something like this:
Foo[][][] firstFoo = new Foo[10][][];
Foo[][][] fooToCopy = new Foo[firstFoo.Length][][];
copyFooArray(firstFoo, ref fooToCopy);
Also, the ref keyword is unnecessary since arrays are passed by reference in c# anyways.
In addition to the fix presented in the accepted answer, here's a faster way to do that:
public static int[][][] Copy(int[][][] source)
{
int[][][] dest = new int[source.Length][][];
for (int x = 0; x < source.Length; x++)
{
int[][] s = new int[source[x].Length][];
for (int y = 0; y < source[x].Length; y++)
{
int[] n = new int[source[x][y].Length];
int length = source[x][y].Length * sizeof(int);
Buffer.BlockCopy(source[x][y], 0, n, 0, length);
s[y] = n;
}
dest[x] = s;
}
return dest;
}
I Write this code in c# , i use list of structure , but how can i add to list with structure,i use add to add xx,yy,size but not correct ? i need only method for add data to list of structure
public struct blocks
{
public Int32 xx;
public Int32 yy;
public Int32 size;
};
namespace test2
{
class Program
{
static List<blocks> blocks1 = new List<blocks>();
static void Main(string[] args)
{
Int32 index=0;
for (int y = 1; y < 5; y++)
for (int x = 1; x < 5; x++)
{
blocks1[index].xx +=( x * 2); // store start point (x) of block
blocks1[index].yy += (y *2); // store start point (y) of block
blocks1[index].size +=( 2); // store block size
index++;
}
}
}
}
I would use a List<blocks> blocks1 = new List<blocks>() since that is a data structure that you can just add to as needed.
You can add to it like so:
blocks newBlock = new blocks();
blocks1.Add(newBlock) ;
This is probably what you are trying to achieve but not very sure. If this is not what you need then please update your question with the information as per my comment
public struct blocks
{
public Int32 xb;
public Int32 yb;
public Int32 size;
};
namespace test
{
class Program
{
static List<blocks> blocks1;
static void Main(string[] args)
{
blocks1 = new List<blocks>();
for (int y = 1; y < 5; y++)
{
for (int x = 1; x < 5; x++)
{
blocks newBlock = new blocks();
newBlock.xb = x * 2;
newBlock.yb = y * 2;
newBlock.size = 2;
blocks1.Add(newBlock);
}
}
}
}
}
Hope this helps.