41

The Problem

I have a multidimensional array similar to the one below. What I'm trying to achieve is a way to find and retrieve from the array the one with the highest "Total" value, now I know there's a function called max but that doesn't work with a multidimensional array like this.

What I've thought about doing is creating a foreach loop and building a new array with only the totals, then using max to find the max value, which would work, the only issue would then be retrieving the rest of the data which relates to that max value. I'm not sure that's the most efficient way either.

Any ideas?

Array
(
    [0] => Array
        (
            [Key1] => Key1
            [Total] => 13
        )

    [1] => Array
        (
            [Key2] => Key2
            [Total] => 117
        )

    [2] => Array
        (
            [Key3] => Key3
            [Total] => 39
        )
)
Karl
  • 5,435
  • 11
  • 44
  • 70
  • If you do not need such a performance - two loops will be good. If you want huge performance gain in arrays with really a lot of elements you should use Interval Trees(Segment Trees) to store values. But this is a huge theory and may not be needed in your case. – Kamen Stoykov Jun 27 '13 at 09:38

9 Answers9

99

Since PHP 5.5 you can use array_column to get an array of values for specific key, and max it.

max(array_column($array, 'Total'))

James
  • 4,644
  • 5
  • 37
  • 48
Astrus
  • 1,412
  • 1
  • 12
  • 9
  • 1
    This is optimized for code brevity, not performance. Calling `array_column()` then `max()` means two full loops over the data. As demonstrated by Robert's answer, the task can be completed by a single iteration over the data. Also, the asker says: `the only issue would then be retrieving the rest of the data which relates to that max value` – mickmackusa Jul 22 '22 at 21:30
31

Just do a simple loop and compare values or use array_reduce. @ is an error suppressor; it hides the fact that $a['total'] is not declared before it is accessed on the first iteration. Demo

$data = array_reduce($data, function ($a, $b) {
    return @$a['Total'] > $b['Total'] ? $a : $b ;
});

print_r($data);
// Array( [Key2] => Key2 [Total] => 117 )

It could also be written with arrow function syntax which has been avaiable since PHP7.4. Demo

var_export(
    array_reduce(
        $data,
        fn($result, $row) =>
            $result['Total'] > $row['Total']
            ? $result
            : $row,
        ['Key1' => null, 'Total' => PHP_INT_MIN]
    )
);
// array ('Key2' => 'Key2', 'Total' => 117,)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Baba
  • 94,024
  • 28
  • 166
  • 217
11

It's so basic algorithm.

$max = -9999999; //will hold max val
$found_item = null; //will hold item with max val;

foreach($arr as $k=>$v)
{
    if($v['Total']>$max)
    {
       $max = $v['Total'];
       $found_item = $v;
    }
}

echo "max value is $max";
print_r($found_item);

Working demo

Robert
  • 19,800
  • 5
  • 55
  • 85
  • 2
    Why would you initialise `$max` with `-9999999`? – halfpastfour.am Oct 24 '14 at 09:03
  • You can init it with "$max= ~PHP_INT_MAX;" Check the basics of finding the min number. The other option is to init it with first member of array – Robert Oct 24 '14 at 14:07
  • No, my question was why? – halfpastfour.am Oct 27 '14 at 13:12
  • because you need to have minimal value to compare with first time and you need to be sure that this value is smaller than first element. It could be done also with assiging the first element of array but then I would need to start loop from second element in array. – Robert Oct 27 '14 at 15:05
  • change `$v['Total']>$max` to `$v['Total']<$max` and put `$max = 9999999999;` you can change variable names for some visiblity $max to $min – Robert Feb 23 '15 at 14:42
  • Or one can use `-Inf` :) – Terry May 27 '16 at 20:46
  • Instead of `-9999` initialize with the first value (in this example if would be `$array[0]["Total"]`) or a special value such as NULL or false (you will then have to check for NULL/false in the `if` clause). – Salman A May 17 '17 at 12:28
  • 3
    You could always just initialize it as `$max = null;` then simply add one more condition to check that `$max` has already been set, like `if($v['Total']>$max || !$max)`. In that case, the first time it runs through the loop and there isn't a `$max` value defined, it will set one. The assumption is that, even if it's the one and only value, it would technically be the max regardless of whatever arbitrary minimum you've hard coded. – Mike Jan 25 '18 at 20:23
  • `!$max` would return true when `$max === 0`. This is not teliable advice @Mike – mickmackusa Jul 21 '22 at 16:09
  • @mickmackusa This is a 4.5 year comment to a solution, and after criticizing a viable solution you still managed to provide no additional value yourself. Casting stones from a glass house, are we? – Mike Jul 22 '22 at 20:34
9

I know this question is old, but I'm providing the following answer in response to another question that pointed here after being marked as a duplicate. This is another alternative I don't see mentioned in the current answers.

I know there's a function called max but that doesn't work with a multidimensional array like this.

You can get around that with array_column which makes getting the maximum value very easy:

$arr = [['message_id' => 1,
             'points' => 3],
        ['message_id' => 2,
             'points' => 2],
        ['message_id' => 3,
             'points' => 2]];

// max value
$max = max(array_column($arr, 'points'));

Getting the associative key is where it gets a little more tricky, considering that you might actually want multiple keys (if $max matches more than one value). You can do this with an anonymous function inside array_map, and use array_filter to remove the null values:

// keys of max value
$keys = array_filter(array_map(function ($arr) use ($max) {
    return $arr['points'] == $max ? $arr['message_id'] : null;
}, $arr));

Output:

array(1) {
  [0]=>
  int(1)
}

If you do end up with multiples keys but are only interested in the first match found, then simply reference $keys[0].

mister martin
  • 6,197
  • 4
  • 30
  • 63
  • Under the hood, this iterates the array twice; once in `array_column()` and once with `max()` -- to derive the max value. Robert's answer has a more efficient time complexity. Calling `array_map()` and `array_filter()` means two more full traverses of the array. I can't endorse making four loops when something can be done in one loop. – mickmackusa Jul 22 '22 at 21:08
3

another simple method will be

$max  = array_map( function( $arr ) {
  global $last;
  return (int)( ( $arr["Total"] > $last ) ? $arr["Total"] : $last );
}, $array );

print_r( max( $max ) );
bystwn22
  • 1,776
  • 1
  • 10
  • 9
  • Under the hood, this iterates the array twice; once in `array_map()` and once with `max()`. Robert's answer has a more efficient time complexity. – mickmackusa Jul 22 '22 at 21:06
2
<?php
$myarray = array(
    0 => array(
        'Key1' => 'Key1',
        'Total' => 13,
    ),
    1 => array(
        'Key2' => 'Key2',
        'Total' => 117,
    ),
    2 => array(
        'Key2' => 'Key3',
        'Total' => 39,
    ),
);

$out = array();
foreach ($myarray as $item) {
    $out[] = $item['Total'];
}

echo max($out); //117

unset($out, $item);
Nabi K.A.Z.
  • 9,887
  • 6
  • 59
  • 81
  • 1
    Under the hood, this iterates the array twice; once in `foreach()` and once with `max()`. Robert's answer has a more efficient time complexity. – mickmackusa Jul 22 '22 at 21:07
1

You can use php usort function: http://php.net/manual/en/function.usort.php

A pretty illustrative example is given here:

<?php
function cmp($a, $b)
{
 return strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

while (list($key, $value) = each($fruits)) {
 echo "\$fruits[$key]: " . $value["fruit"] . "\n";
}
?>

So it will sort the max value to the last array index.

Output:

$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons

This example is given on aforementioned link

geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
Umair Khan
  • 473
  • 2
  • 15
  • 1
    I wouldn't recommend using a complex sort algorithm for something like getting a single value out of an array. Actually, sorting should only be done when really needed. The additional overhead isn't worth a "cool-looking" solution. – Marcello Mönkemeyer Oct 11 '16 at 08:09
  • @MarcelloMönkemeyer what exactly is the additional overhead which you are referring here? To get a single value(max or min) whole array needs to be traversed (by comparing). The function above also does the same thing, unless i am missing something here! – Umair Khan Oct 12 '16 at 15:25
  • 1
    The actual sorting, because it requires multiple traversals and comparisons as well as repeatedly moving and repositioning array keys. Depending on the implemented sorting algorithm (unfortunately, I don't know which one PHP uses), this can grow exponential. It may not be of any visible difference for small arrays and when used few times, but imagine it being constantly used in a framework like Symfony or Laravel - that'd be a _huge_ bummer. – Marcello Mönkemeyer Oct 13 '16 at 13:48
1

Can be done using array_walk(array_walk_recursive if needed)

$arr is the array you want to search in

$largestElement = null;

array_walk($arr, function(&$item, $key) use (&$largestElement) {
    if (!is_array($largestElement) || $largestElement["Total"] < $item["Total"]) {
        $largestElement = $item;
    }
});
katona.abel
  • 771
  • 4
  • 12
0

array_reduce accepts a 3rd "initial" parameter. Use this to avoid the bad practice of using "@" error suppression :

$data = array_reduce($data, function ($a, $b) {
    return $a['Total'] > $b['Total'] ? $a : $b ;
},['Total' => 0]);

print_r($data);

PHP 7.4

$data = array_reduce($data, fn(a,b) => $a['Total'] > $b['Total'] ? $a : $b, ['Total' => 0]);
wLc
  • 968
  • 12
  • 15