This one is hard to explain! Sorry for that, but here goes...
I have a 3D array of some data [X][Y][Z], and I like to check for about 10 different combination conditions and keep only data when it's a match... Example:
X Y Z
//myData[1..1000000][1..10][1..10].foo // foo is an int
X[i].Y[ii].Z[iii].foo; // X is a container, Y= 1 to 10 levels. And Z= objects
//I want to apply a "filter" to the Z objects...
Lets say I want to find all combinations where the sum of "foo" is smaller and larger than two numbers, and only keep those Z objects
For next iteration i want to find lets say only where "foo" is a prime number, still keeping only Z objects
And so on for more conditions, resulting in smaller and smaller list.
It doesn't matter in wich order they are performed.
I sort of know how to do it, but I end up in some really nasty loops...
Any Ideas? Maybe adding to another list is faster than deleting from original list?
Thanks in advance!
When you want to chain together logic like this, I think you really want to use Linq. Unfortunately, it can be cumbersome to use Linq on multidimensional arrays. With some helper methods, though, we can convert the data array into something more usable. First, let's build a wrapper class for any object that has 3 dimensions associated with it:
public class ThreeDimensionalArrayExtension<T> {
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public T Value { get; set; }
}
Next, let's write a helper method that converts 3-dimsensional arrays to IEnumerables of the new type:
public static class ThreeDimensionalArrayExtensionMethods {
public static IEnumerable<ThreeDimensionalArrayExtension<T>> ConvertArray<T>(this T[,,] foos) {
for(var x = 0; x < foos.GetLength(0); x++) {
for (var y = 0; y < foos.GetLength(1); y++) {
for (var z = 0; z < foos.GetLength(2); z++) {
yield return new ThreeDimensionalArrayExtension<T> { X = x, Y = y , Z = z, Value = foos[x, y, z] };
}
}
}
}
}
Note that since we are using an iterator block (the yield-return pattern), calling this method does not actually perform any computation.
Now we can use the power of Linq on your 3-dimensional array to filter it however we want!
myData.ConvertArray().Where(d => d.Value.Foo > 5)
.Where(d => IsPrime(d.Value.Foo))
.Where(...);
Edit:
I see that you're using 3 nested classes and not a multidimensional array which I assumed you were using. The goal should still be to convert that object to an IEnumerable upon which you can very easily chain Linq queries to filter or project the data. In your case, you can do:
public static class ThreeDimensionalArrayExtensionMethods {
public static IEnumerable<ThreeDimensionalArrayExtension<X>> ConvertArray(this X[] foos) {
for(var x = 0; x < foos.Count(); x++) {
for (var y = 0; y < foos[x].Count(); y++) {
for (var z = 0; z < foos[x][y].Count(); z++) {
yield return new ThreeDimensionalArrayExtension<T> { X = x, Y = y , Z = z, Value = foos[x][y][z] };
}
}
}
}
}
And then use the same call to ConvertArray followed by the filtering Where clauses described above.
If you don't care about the X/Y/Z indices, you can also just use SelectMany to project the multidimensional list onto a single dimensional list:
X.SelectMany(y => y.SelectMany(z => z)).Where(z => z.Foo > 5);
Related
I'm following Sebastian Lague's Procedural Landmass Generation tutorial . He is generating landmass' color by height value. But I want to separate landmass to a array or list as areas by their color. Because the way Sebastian use generates too many water or mountain areas so I want to make them less. I tried to edit the code of him but the code I made tooks 2-3 minutes to separate. Does anyone have idea to make it faster?
The class I use to seperate regions and the areas in every region:
public class Positions
{
public int regionID;
public int areaID;
public int x;
public float y;
public int z;
}
List<Positions> positions = new List<Positions>();
The code I try to find and list them:
for (int y = 0; y<mapChunkSize; y++)
{
for (int x = 0; x<mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i<regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
int areaID = 0;
if(positions.Where(x => x.regionID == i).Count() != 0)
{
areaID = getNeighourIndex(i, x, y);
}
Positions p = new Positions { regionID = i, areaID = areaID, x = x, y = noiseMap[x, y], z = y };
positions.Add(p);
break;
}
}
}
}
int getNeighourIndex(int regionID, int x, int y)
{
List<Positions> pos = positions.Where(x => x.regionID == regionID).ToList();
if (pos.Where(q => Vector2Int.Distance(new Vector2Int(q.x, q.z), new Vector2Int(x, y)) <= 1).Count() > 0)
return pos.Find(q => Vector2Int.Distance(new Vector2Int(q.x, q.z), new Vector2Int(x, y)) <= 1).areaID;
return (pos.Select(q => q.areaID).OrderBy(x => x).LastOrDefault()) + 1;
}
As always when doing optimizing, the first step should be to measure, ideally with a profiler since this can hint at what it is that takes most time.
But I would guess that the majority of the time is spent in positions.Where(x => x.regionID == regionID). To solve this you could use a multi value dictionary, i.e. a dictionary where each key can map to multiple values. It is fairly easy to make your own wrapper around a Dictionary<TKey, List<TValue>>, or you could use one from Microsoft.Experimental.Collections.
You could also consider using a hierarchical search structure for the points, like a kd-tree, quad-tree or similar.
Also, if performance is of high importance, using LINQ is likely not the best option. LINQ is convenient, but the abstraction adds some overhead. In most cases this overhead is irrelevant, but in tight loops like this it may very well be a significant factor.
I have this line of code and I want under certain conditions to initialize the whole array to true in one line,
How can I do that?
public bool[,] OptionalHours { get; set; } = new bool[6, 24];
Technically, this would do it:
for(int i = 0, x = _optionalHours.GetLength(0), y = _optionalHours.GetLength(1); i < x * y; _optionalHours[i/y, i%y] = true, i++);
It gets shorter if you're happy to fix the 6 and 24 and jiggle some ops around:
for(int i = 6*24; --i >= 0; _optionalHours[i/24, i%24] = true);
Would you use it though? Well; could you understand it immediately and are happy to explain it to anyone else who asks? If yes, then use it. If no then go for something more expected:
for(int x = _optionalHours.GetUpperBound(0); x >= 0; x--)
for(int y = _optionalHours.GetUpperBound(1); y >= 0; y--)
_optionalHours[x, y] = true;
There aren't any prizes at code review for the person who wrote the most WTF..
It's also quite a small array (hours in a week?) so you could also init it with this one-liner that's easier to figure:
var _optionalHours = new[,]{
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true},
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true},
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true},
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true},
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true},
{true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true}
};
As an idle curiosity, you could also do it "mathematically", wrap the array in a class that will invert the bools if some condition is set:
public class SixByTwentyFour
{
private bool[,] _optionalHours { get; set; } = new bool[6, 24];
public bool CertainCondition;
public bool this[int x, int y]{
get => _optionalHours[x,y] ^ CertainCondition;
set { _optionalHours[x,y] = value ^ CertainCondition; }
}
}
You can access an instance of this class as though it were a 2D array:
var sbtf = new SixByTwentyFour();
sbtf.CertainCondition = true;
Console.WriteLine(sbtf[1,1]); //prints true
This one doesn't give you a "Properties should not return arrays" warning if you have an analayzer enabled that looks for such :D
I am writing a function that calculates distance from x and y coordinate. I have a two dimensional array that has bunch of x and y coordinates.
Function returns a list of x and y coordinates in the order of distance from a point. To calculate the distance from each point there is a formula(Square root of sum of coordinates).
I can calculate the distance for each x and y coordinate. I am adding that to list. How do I store distance as another property as it is for that particular coordinate and then sort it.
public static List<List<int>> calculateDistance(int[,] Coordinates)
{
List<List<int>> result = new List<List<int>>();
int bound0 = Coordinates.GetUpperBound(0);
List<double> distance = new List<double>();
for (int i = 0;i <= bound0; i++)
{
distance.Add(Math.Sqrt(Coordinates[i, 0]) + Coordinates[i,1]));
}
return result;
}
}
Based on your description you don't need to create a calculateDistance method. Formula to calculate distance can be given with lambda expression. Anywhere in your code you can create the list you need and get it sorted with Linq.
Example
var list = Enumerable
.Range(0, Coordinates.GetLength(0))
.Select(i => new { X = Coordinates[i, 0], Y = Coordinates[i, 1], D = Math.Sqrt(Math.Pow(Coordinates[i, 0], 2) + Math.Pow(Coordinates[i, 1], 2)) });
Math.Sqrt(Math.Pow(Coordinates[i, 0], 2) + Math.Pow(Coordinates[i, 1], 2)) is used here for demonstration. Instead use your own expression to calculate the distance.
To sort this you can simply use
var list2 = list.OrderBy(a => a.D);
It would be more helpful if you try to define proper data structures for your inputs and your output. It could be something as simple as a tuple, or something more idiomatic like a struct or a class.
struct Coordinate {
public Coordinate(int x, int y) {
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
}
Then define a result structure, something like:
struct Result {
public Result(Coordinate coordinate, double distance) {
Coordinate = coordinate;
Distance = distance;
}
public Coordinate Coordinate { get; }
public double Distance { get; }
}
Then you can create a list of those result items like:
public List<Result> ComputeDistances(List<Coordinate> coordinates) {
List<Result> results = new List<Result>();
foreach (var coordinate in coordinates) {
double distance = Math.Sqrt(coordinate.X + coordinate.Y); // *
results.Add(new Result(coordinate, distance));
}
return results;
}
(*) Note that the specified distance function is a little bit odd. Normally you would like to sum the squares of the X and Y coordinate
If you like a more functional style, you can change that code quite a bit:
public IEnumerable<Result> ComputeDistances(IEnumerable<Coordinate> coordinates) {
return from coordinate in coordinates
let distance = Math.Sqrt(coordinate.X + coordinate.Y)
select new Result(coordinate, distance);
}
Changing from List to IEnumerable allows you with the proper care, to delay the execution of the computation.
After you have the sequence of results, the easiest way to sort them is using the OrderBy extension method.
public IEnumerable<Result> SortByDistance(IEnumerable<Result> results)
{
return results.OrderBy(result => result.Distance);
}
And then combine all:
List<Coordinate> coordinates = .... // get the list of coordinates somehow;
IEnumerable<Result> distances = ComputeDistances(coordinates);
IEnumerable<Result> sortedByDistance = SortByDistance(distances);
// if you want to get back a list, in order to avoid enumerating multiple times
List<Result> results = sortedByDistance.ToList();
Something like:
forelement (element G_Element, Grid)
{
Grid[G_Element.dim1, G_Element.dim2] =
new clsGridElement(G_Element.dim1, G_Element.dim2);
}
instead of
for (int X = 0; X < GridWidth; X++)
for (int Y = 0; Y < GridHeight; Y++)
Grid[X, Y] = new clsGridElement(X, Y);
If something doesn't innately exist, is that something that could be created?
Thanks,
Tim
You could do this - just make a custom type that exposes these, and use a regular foreach:
public class TwoDimensionalIterator
{
public TwoDimensionalIterator(int i, int j)
{
this.Dim1 = i; this.Dim2 = j;
}
public int Dim1 { get; private set; }
public int Dim2 { get; private set; }
}
Then make an extension method somewhere to return an enumerable of this:
public static IEnumerable<TwoDimensionalIterator> IterateTwoDim<T>(this T[,] array)
{
for (int i=0;i<array.GetLength(0);++i)
for (int j=0;i<array.GetLength(1);++j)
yield return new TwoDimensionalIterator(i,j);
}
With this, you could then do:
foreach(var index in Grid.IterateTwoDim())
{
Grid[index.Dim1, index.Dim2] = new clsGridElement(index.Dim1, index.Dim2);
}
Not sure exactly what you are trying to do here, or why, or what you expect to get from it, but if you implement your own iterator than implements the IEnumerator interface then you could create something that would hit every cell in your 2D (or more) collection.
Of course, you won't actually gain anything performance-wise from doing this versus just using nested loops, but I guess it'd be syntactic sugar.
Such creation of indexes can be obtained by using "Cartesian product" of all indexes. Here is sample based on Is there a good LINQ way to do a cartesian product? (courtesy of the Eric Lippert):
var allIndexes = from dim1 in Enumerable.Range(0, Grid.GetLength(0))
from dim2 in Enumerable.Range(0, Grid.GetLength(1))
select new {dim1, dim2};
foreach (var G_Element in allIndexes)
{
Grid[G_Element.dim1, G_Element.dim2] =
new clsGridElement(G_Element.dim1, G_Element.dim2);
}
I am required to create a program which reads in data from a .cvs file, and use these (x, y and z) values for a series of calculations.
I read in the file as a string, and then split this into 3 smaller strings for x, y and z.
The x, y and z coordinates represents the x and y coordinates of the contours of a lake, and the depth (z).
One of the calculations which I have to do, is to calculate the surface area of the lake, using the formula (x[i]*y[i+1])-(x[i+1]*y[i]), where z(depth) = 0.
I can get my code to run perfectly, up until the x[i+1] and y[i+1], where it keeps giving me a value of 0.
Can someone please tell me how to fix this?
Here is my code;
{
string[] ss = File.ReadAllLines(#"C:File.csv");
for (int i = 1; i < ss.Length; i++)
{
string[] valuesAsString = ss[i].Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
double[] X = new double[valuesAsString.Length];
double[] Y = new double[valuesAsString.Length];
double[] Z = new double[valuesAsString.Length];
for (int n = 0; n < 1; n++)
{
X[n] = double.Parse(valuesAsString[0]);
Y[n] = double.Parse(valuesAsString[1]);
}
do
{
double SurfaceArea = (X[n] * Y[n + 1]) - (X[n + 1] * Y[n]);
Console.WriteLine(SurfaceArea);
}
while (Z[n] == 0);
}
}
Ok, im not sure if i got it right, so you if you would take a look to what i did and tell me if its of any help.
After reviewng it a little i came up with the following:
A class for the values
public class ValueXyz
{
public double X { get; set; }
public double Y { get; set; }
public int Z { get; set; }
}
A class to manange the calculation:
public class SurfaceCalculator
{
private ValueXyz[] _valuesXyz;
private double _surface;
private readonly string _textWithValues;
public SurfaceCalculator(string textWithValues)
{
_textWithValues = textWithValues;
SetValuesToCalculate();
}
public double Surface
{
get { return _surface; }
}
public void CalculateSurface()
{
for (var i = 0; i < _valuesXyz.Length; i++)
{
if (_valuesXyz[i].Z == 0)
_surface = (_valuesXyz[i].X*_valuesXyz[i + 1].Y) - (_valuesXyz[i + 1].X*_valuesXyz[i].Y);
}
}
private void SetValuesToCalculate()
{
var valuesXyz = _textWithValues.Split(' ');
_valuesXyz = valuesXyz.Select(item => new ValueXyz
{
X = Convert.ToDouble(item.Split(',')[0]),
Y = Convert.ToDouble(item.Split(',')[1]),
Z = Convert.ToInt32(item.Split(',')[2])
}).ToArray();
}
}
So now your client code could do somethin like:
[TestMethod]
public void TestSurfaceCalculatorGetsAValue()
{
//var textWithValues = File.ReadAllText(#"C:File.csv");
var textWithValues = "424.26,424.26,0 589.43,231.46,0 720.81,14.22,1";
var calculator = new SurfaceCalculator(textWithValues);
calculator.CalculateSurface();
Assert.IsNotNull(calculator.Surface);
}
I'm not very sure i got the idea correct of how to implement the formula, but i just wanted to expose an alternative you can use, you can never have to many ways of doing one thing :).
Cheers.
By the way part of the intent i had, was not tying up your funcionality to the csv in case your source for the text in the future would change.
Step through your code in the debugger. Pay special attention to tbe behavior of the line
for (int n = 0; n < 1; n++)
This loop will execute how many times? What will the value of n be during each iteration through the loop?
Well, one thing i noticed is when you're setting your X, Y, Z vars, you're setting it to the Length of the array object instead of it's value - is that intentional?
Put a debug break on the line with:
double SurfaceArea = (X[n] * Y[n + 1]) - (X[n + 1] * Y[n]);
and check the datatype of "X", "Y" and "Z"
I've had problems in the past where it tries to calculate them as strings (because it took it out of the data source as strings). I ended up fixing it by adding CInt() to each of the variables (or Convert.ToInt32();).
Hope this helps.
As this looks like it might be a homework problem, I am trying not to give a direct solution in my answer, but I see a number of questionable parts of your code that you should examine.
Why are X, Y, Z arrays? You are creating a new array each time through the outer loop, setting the length of the array to the number of elements in the line, then only assigning a value to one element of X and Y, and never assigning Z to anything.
As phoog suggests in his answer, what is the purpose of: for (int n = 0; n < 1; n++)?
What are you trying to accomplish with the do-while loop? As it has been mentioned in the comments by Mr Skeet, X[n], Y[n], Z[n] don't exist because n does not exist outside of the loop it is declared for. Even if it did exist Z[n] will always be zero because you never assign anything to the Z array after it is initialized, so the do-while loop will run forever.