Reputation: 18228
I have a multi dimensions array ins C# defined as follow:
double[,,] myArray=new double[10000,10000,3];
I find the maximum value of this array when the last dim is for example is 0. something g such as this:
double m1=myArray[?,?,0].Max();
How can I calculate it using Linq or other methods?
Upvotes: 1
Views: 234
Reputation: 35746
EDIT 2
Note, this extension works equally well for the hideous but valid case of a non zero based array,
var nonZeroBasedArray = Array.CreateInstance(
typeof(double),
new[] { 4, 4, 3 },
new[] { -2, -2, 0 });
Note that the first two dimensions range from -2
to 1
inclusive (yikes.) This test code illustrates that the Flatten
extension still works.
var count = 0;
foreach (var element in nonZeroBasedArray.Flatten<double>(null, null, 0))
{
Console.Write(string.Join(", ", element.Key));
Console.WriteLine(": {0}", element.Value);
}
Console.WriteLine("Count: {0}", count);
Console.ReadKey();
EDIT
So, using the extension defined below you can do
var myArray = new double[10000,10000,3];
var ordered = myArray.Flatten<double>(null, null, 0).OrderBy(p => p.Value);
var maxZ0 = ordered.First();
var minZ0 = ordered.Last();
The element type is a KeyValuePair<IEnumerable<int>, T>
so the Key
allows you to back reference to the original array.
Ok, here is a generic extension, intially inspired by p.s.w.g's answer
If you start with Eric Lippert's inspirational CartesianProduct<T>
extension,
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[]
{
item
}));
}
Then you make a function to generate the bound sets of a multi dimensional array that allows you to specify fixed values for some dimensions.
private static IEnumerable<IEnumerable<int>> GetBoundSequences(
Array array,
int?[] definedBounds)
{
for (var rank = 0; rank < array.Rank; rank++)
{
var defined = definedBounds.ElementAtorDefault(rank);
if (defined.HasValue)
{
yield return new[] { defined.Value };
}
else
{
var min = array.GetLowerBound(rank);
yield return Enumerable.Range(
min,
(array.GetUpperBound(rank) - min) + 1);
}
}
}
you can use both to make a flexible Flatten<T>
extension, that works with arrays of any rank.
public static IEnumerable<KeyValuePair<IEnumerable<int>, T>> Flatten<T>(
this Array array,
params int?[] definedBounds)
{
var coordSets = GetBoundSequences(array, definedBounds).CartesianProduct();
foreach (var coordSet in coordSets)
{
var coords = coordSet.ToArray();
var value = (T)array.GetValue(coords);
yield return new KeyValuePair<IEnumerable<int>, T>(
coords,
value);
}
}
Once you have this, you can do something like
var myArray = new double[10000,10000,3];
var maxZ0 = myArray.Flatten<double>(null, null, 0).Max(p => p.Value);
This is good because it lazily iterates and converts only the elements specified.
Upvotes: 2
Reputation: 149108
If you'd like to get the max across some subset of the array you can do this:
double m1 =
(from x in Enumerable.Range(0, myArray.GetLength(0))
from y in Enumerable.Range(0, myArray.GetLength(1))
select myArray[x, y, 0])
.Max();
If you'd like to get the max across all elements in the array you can just do this
double m1 = myArray.Cast<double>().Max();
However, you can get a significant performance boost by implementing your own extension method like this:
public static IEnumerable<T> Flatten<T>(this T[,,] arry) {
foreach(T x in arry) yield return item;
}
myArray.Flatten().Max();
Upvotes: 2
Reputation: 4726
Try this
double[,,] myArray = new double[10000, 10000, 3];
double max = myArray.Cast<double>().Max();
Upvotes: 0