Get the minimum and maximum values in an array column
Asked Answered
Y

7

8

I have an array in this format:

$array = [
    ['id' => 117, 'name' => 'Networking', 'count' => 16],
    ['id' => 188, 'name' => 'FTP', 'count' => 23],
    ['id' => 189, 'name' => 'Internet', 'count' => 48],
];

Is there a good way to retrieve the minimum and maximum values of the 'count' column (16 and 48 respectively)?

I could do this using a few loops, but I wonder if there may be a better way.

Ybarra answered 28/8, 2009 at 6:19 Comment(3)
+1 I ran into this problem today and I just did a loop. Curious on what other methods there are.Heaveho
If there is some function that do this it will be implemented with loops, so what is so bad using your own functions?!Alike
@Svetlozar Angelov: For PHP functions the loops will be written in C, not PHP, so they are generally faster.Emmen
B
6

In contrast to what others have posted, you cannot use the min()/max() functions for this problem as these functions do not understand the datastructure (array) which are passed in. These functions only work for scalar array elements.


BEGIN EDIT

The reason why the use of min() and max() seem to yield the correct answer is related to type-casting arrays to integers which is an undefined behaviour:

The behaviour of converting to integer is undefined for other types. Do not rely on any observed behaviour, as it can change without notice.

My statement above about the type-casting was wrong. Actually min() and max() do work with arrays but not in the way the OP needs them to work. When using min() and max() with multiple arrays or an array of arrays elements are compared element by element from left to right:

$val = min(array(2, 4, 8), array(2, 5, 1)); // array(2, 4, 8)
/*
 * first element compared to first element: 2 == 2
 * second element compared to second element: 4 < 5
 * first array is considered the min and is returned
 */

Translated into the OP's problem this shows the reason why the direct use of min() and max() seems to yield the correct result. The arrays' first elements are the id-values, therefore min() and max() will compare them first, incidentally resulting in the correct result because the lowest id is the one with the lowest count and the highest id is the one with the highest count.

END EDIT


The correct way would be to use a loop.

$a = array(
        array('id' => 117, 'name' => 'Networking', 'count' => 16),
        array('id' => 188, 'name' => 'FTP', 'count' => 23),
        array('id' => 189, 'name' => 'Internet', 'count' => 48)
);
$min = PHP_INT_MAX;
$max = 0;
foreach ($a as $i) {
    $min = min($min, $i['count']);
    $max = max($max, $i['count']);
}
Bucephalus answered 28/8, 2009 at 7:24 Comment(4)
From the manual page for max $val = max(array(2, 4, 8), array(2, 5, 7)); // array(2, 5, 7). Read it.Heaveho
Your answer is the correct one though not for the reason you stated about. min and max do handle arrays correctly and do not cast them to integers, the manual gives more detail.Heaveho
@MitMaro: You're right - my assumption or reasoning about the use of arrays in min() and max() was wrong. I edited my answer and removed the wrong statement.Bucephalus
As a "tinfoil hat" argument, if the input array is empty, this answer will return incorrect results. Of course, I don't know if it is possible for the OP's application to pass in an empty array. This is just a caution for researchers.Cyclopropane
C
1

As you can see from the earlier answers, there are many viable techniques to consider.

If you are considering best performance, then you should be minimizing the number of loops that are used and the number of function calls.

"Under the hood", the native functions min() and max() will be fully iterating the array that they are fed. So, if you use a function or construct to loop over the 2-dimensional input array, then call min(), then call max(), you are iterating the full length of the input array three separate times.

Consider this snippet that only iterates 1 time:

Code: (Demo)

$min = array_shift($array)['count'] ?? null;
$max = $min;
foreach ($array as $row) {
    if ($min > $row['count']) {
        $min = $row['count'];
    }
    if ($max < $row['count']) {
        $max = $row['count'];
    }
}
var_export(['min' => $min, 'max' => $max]);
  • The advantages of above are
    • only 1 function call (to extract the first row's data) and one loop
    • it never encounters the same data more than once
    • simple numeric comparisons are very simple/fast for php to execute
  • The disadvantages of above are
    • it mutates the input array by calling array_shift(), so if you re-use the array in the same scope, it will no longer contain the first value
    • it is more verbose than some other techniques and ignores some viable native functions

Another technique places value on functionally styled code. PHP has native functions that make tidy work of this task.

array_column() can isolate all of the values in the count column. min() returns the lowest number. max() returns the highest number.

Code: (Demo)

$counts = array_column($array, 'count');
var_export(['min' => min($counts), 'max' => max($counts)]);
  • The advantages of above are
    • only 1 temporary variable
    • it does not mutate the input array
    • very concise, intuitive, and declarative coding style
    • it will not generate any warnings if one of the rows is missing the count element
  • The disadvantages of above are
    • it technically iterates the data three times
    • it very likely performs worse that the first snippet
Cyclopropane answered 4/3, 2021 at 10:37 Comment(0)
C
0

You could use the max() and min() functions.

Columnar answered 28/8, 2009 at 6:21 Comment(1)
This is a partial answer at best.Cyclopropane
P
0

What did you do with a few loops? One is quite enough :)

  1. Get the first element, assing the count to both $min and $max
  2. iterate over the rest, compare count to each $min and $max, if smaller/larger, assign the new count value
Purple answered 28/8, 2009 at 6:27 Comment(1)
This seems reasonably well-explained, but without a snippet, researchers may not implement your advice the way you intend. You may or may not endorse the first snippet in my answer. If you have a different take, perhaps add a snippet to your answer and explain why you would do it your unique way.Cyclopropane
G
0

Looks like you can't use max() on a 2D array. It just returns the largest array, not the max() of each index (as being mentioned in a few answers).

So:

$count = array();
foreach($arr as $_arr) {
    $count[] = $_arr['count'];
}
var_dump(max($count), min($count));
Gurgitation answered 28/8, 2009 at 10:15 Comment(0)
Z
0

If the desired column is first in the array you can use the following one-liners:

$max = max(array_map('current', $a));
$min = min(array_map('current', $a));

This will find the min/max of id

Zygophyte answered 16/2, 2011 at 18:50 Comment(1)
Why call array_map() twice for the same return value? This lacks efficiency and is not D.R.Y.Cyclopropane
G
-1

Is there an equivalent build-in function to that one? (even without the test capability)

/**
 * extracts a column from a 2D array, with an optional selection over another column
 *
 * @param $aArray     array to extract from
 * @param $aColName   name of the column to extract, ex. 'O_NAME'
 * @param $aColTest   (optional) name of the column to make the test on, ex. 'O_ID'
 * @param $aTest      (optional) string for the test ex. ">= 10", "=='".$toto."'"
 * @return            1D array with only the extracted column
 * @access public
 */

  function extractColFromArray($aArray, $aColName, $aColTest="", $aTest="") {
  $mRes = array();
  foreach($aArray as $row) {
   if (($aColTest == "") || (eval("return " . $row[$aColTest] . $aTest . ";" )) ) {
    $mRes[] = $row[$aColName];
   }
  }
  return $mRes;
 } // extractColFromArray
Gingery answered 7/10, 2009 at 8:17 Comment(1)
This snippet does not find the min and max values in a 2-dim array. This is not a correct answer to this question.Cyclopropane

© 2022 - 2024 — McMap. All rights reserved.