Reputation: 423
I am working with multi-dimentioned arrays of bool
, int
, and various struct
. The code loops through these arrays and performs some operation on specific values. For instance,
for (int x = 0; x < this.Size.Width; x++) {
for (int y = 0; y < this.Size.Height; y++) {
if (this.Map[x, y]) {
DrawTerrain(this.Tile[x, y].Location, Terrain.Water);
}
}
}
I can do simple LINQ stuff, but I can't do what I would like. What I would like to do is use LINQ. Maybe something like
from x in this.Map where x == true execute DrawTerrain(...)
But, I don't understand how I can get the x and y locations or how to call a method in the LINQ statement.
Also, it would be great if I can put this code into a function and be able to call it with a delegate or a predicate? I don't know if delegate or predicate are the correct words.
void Draw(Delegate draw, bool[,] map, struct[,] tiles)
from x in map where x == true draw(titles[x,y]).invoke;
}
Upvotes: 5
Views: 3902
Reputation: 243096
The LINQ query syntax isn't really designed for working with 2-dimensional data structures, but you can write an extension method that will convert the 2D array into a sequence of values that contain the coordinates in the original 2D array and the value from the array. You'll need a helper type to store the data:
class CoordinateValue<T> {
public T Value { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
Then you can write the extension method (for any 2D array) like this:
IEnumerable<CoordinateValue<T>> AsEnumerable(this T[,] arr) {
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(0); j++)
yield new CoordinateValue<T> { Value = arr[i, j]; X = i; Y = j; };
}
Now you can finally start using LINQ. To get coordinates of all elements from the 2D array such that the value stored in the element is true
, you can use the following:
var toDraw = from tile in this.Map.AsEnumerable()
where tile.Value == true
select tile;
// tile contains the coordinates (X, Y) and the original value (Value)
foreach(var tile in toDraw)
FillTile(tile.X, tile.Y);
This lets you specify some interesting conditions when filtering the tiles. For example if you wanted to get only diagonal elements, you could write:
var toDraw = from tile in this.Map.AsEnumerable()
where tile.Value == true && tile.X == tile.Y
select tile;
To answer your second question - if you want to encapsulate the behavior into the method, you'll probably need to use some Action<...>
delegate to represent the drawing function that will be passed to the method. It depends on the type signature of your drawing method though.
Upvotes: 1
Reputation: 32296
You can do the following Linq query to get the x,y values then iterate over them to run your method.
var points = from x in Enumerable.Range(0, this.Size.Width - 1)
from y in Enumerable.Range(0, this.Size.Width - 1)
where this.Map[x, y]
select new { X = x, Y = y };
foreach (var p in points)
DrawTerrain(this.Tile[p.X, p.Y].Location, Terrain.Water);
Upvotes: 1
Reputation: 1502206
Well, you could do it with LINQ if you work hard enough, but it would be a pain. It sounds like your first bit of code is absolutely fine.
Having a version of that which is generalised to take an action seems very reasonable though:
public delegate void Drawer(int x, int y, Tile tile);
public void Draw(Drawer drawer, bool[,] map, Tile[,] tiles) {
for (int x = 0; x < this.Size.Width; x++) {
for (int y = 0; y < this.Size.Height; y++) {
if (this.Map[x, y]) {
drawer(x, y, tiles[x, y]);
}
}
}
}
If you really want the LINQ version, it would be something like:
var query = from x in Enumerable.Range(0, Size.Width)
from y in Enumerable.Range(0, Size.Height)
where Map[x, y]
select new { x, y };
foreach (var pair in query)
{
DoWhatever(pair.x, pair.y, tiles[pair.x, pair.y]);
}
Upvotes: 1