Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to count all values in a multidimensional array?

I have tried solutions to many similar questions, but they all seem to give me a count for each array. So I have the following array:

Array
(
    [1] => Array
        (
            [0] => 1
            [1] => 12
            [2] => 2
        )

    [2] => Array
        (
            [0] => 1
            [1] => 13
            [2] => 3
        )

    [3] => Array
        (
            [0] => 1
            [1] => 12
            [2] => 2
        )

    [4] => Array
        (
            [0] => 1
        )

    [5] => Array
        (
            [0] => 1
        )

)

I am trying to count the duplicates across all arrays. So the output should show:

Five 1's
Two 12's
One 13
Two 2's

At the moment I am trying:

foreach($data as $key => $row) {
    print_r(array_count_values($row));
}

Which outputs the counts for each individual array

Array
(
    [1] => 1
    [12] => 1
    [2] => 1
)
Array
(
    [1] => 1
    [13] => 1
    [3] => 1
)
Array
(
    [1] => 1
    [12] => 1
    [2] => 1
)
Array
(
    [1] => 1
)
Array
(
    [1] => 1
)

I have also tried this:

foreach ($data as $key => $row) {
    $counts = array_count_values(array_column($data, $key));
    var_dump($counts);
}

Which seems to miss a lot of information, like the count of the 1's

array(2) {
  [12]=>
  int(2)
  [13]=>
  int(1)
}
array(2) {
  [2]=>
  int(2)
  [3]=>
  int(1)
}
array(0) {
}
array(0) {
}
array(0) {
}

As a note, the initial array keys will not always be sequential, as this represents a row number. So this array may contain rows 1, 2, 5, 6, 7 etc.

How would I go about counting all duplicates together?

like image 743
katie hudson Avatar asked Aug 24 '26 13:08

katie hudson


2 Answers

Since your array is not flattened, you will need to visit each value and increment unless you want to call merging functions.

Code: (Demo)

$array = [
    1 => [1, 12, 2],
    2 => [1, 13, 3],
    3 => [1, 12, 2],
    4 => [1],
    5 => [1]
];

//           make the generated value available outside of function scope
//           \-------------------------------v--------------------------/
array_walk_recursive($array, function($v)use(&$output) {  // visit each leafnode
    if (isset($output[$v])) {  // check if the key has occurred before
        ++$output[$v];         // increment
    } else {
        $output[$v] = 1;       // declare as 1 on first occurrence
    }
});

var_export($output);

Output:

array (
  1 => 5,
  12 => 2,
  2 => 2,
  13 => 1,
  3 => 1,
)

Or, non-recursively:

foreach ($array as $row) {
    foreach ($row as $v) {
        if (isset($output[$v])) { // check if the key has occurred before
            ++$output[$v];        // increment
        } else {
            $output[$v] = 1;      // declare as 1 on first occurrence
        }
    }
}

Or, a functional one-liner to flatten then count:

var_export(array_count_values(array_reduce($array, 'array_merge', array())));

Or, a functional one-liner with the splat operator to flatten then count:

var_export(array_count_values(array_merge(...$array)));
like image 71
mickmackusa Avatar answered Aug 27 '26 02:08

mickmackusa


You can do this quite easily by using an accumulator array and iterating all the elements:

$result = [];
foreach ($data as $row) {
    foreach($row as $value) {
        $result[$value] = isset($result[$value]) ? $result[$value] + 1 : 1;
    }
}
var_dump($result);
like image 36
msg Avatar answered Aug 27 '26 02:08

msg