mans
mans

Reputation: 18228

how to get the min and max of a multi dim array (with one dim is specified) in c#

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

Answers (3)

Jodrell
Jodrell

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

p.s.w.g
p.s.w.g

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

Mez
Mez

Reputation: 4726

Try this

double[,,] myArray = new double[10000, 10000, 3];

double max = myArray.Cast<double>().Max();

Upvotes: 0

Related Questions