mephisto4
mephisto4

Reputation: 22

Count distinct key-value pairs in a 2d array

I need to count the unique key-value pairs in an array of arrays (regardless of which row they come from).

Sample input:

$info = [
    ['car' => 'Audi', 'previous_car' => 'BMW'],
    ['car' => 'Audi', 'previous_car' => 'Seat'],
    ['car' => 'Audi', 'previous_car' => 'BMW'],
    ['car' => 'BMW',  'previous_car' => 'BMW'],
    ['car' => 'Ford', 'previous_car' => 'Seat'],
];

Desired output:

[
    'car' => [
        'Audi' => 3,
        'BMW' => 1,
        'Ford' => 1
    ],
    'previous_car' => [
        'BMW' => 3,
        'Seat' => 2
    ]
]

I tried to use array_value_count(), but I doesn't work well on multidimensional arrays.

Upvotes: 0

Views: 2578

Answers (5)

mickmackusa
mickmackusa

Reputation: 48100

I don't think I'd recommend any native functions for this task. Using nested foreach() loops will probably be cleanest. Use each row's key and value as the first and second level keys and increment tally with each occurrence.

Code: (Demo)

$result = [];
foreach ($info as $row) {
    foreach ($row as $k => $v) {
        $result[$k][$v] = ($result[$k][$v] ?? 0) + 1;
    }
}
var_export($result);

Upvotes: 0

Marcel
Marcel

Reputation: 5119

In a more object orientated way you can solve it as follows

$values = new ArrayObject();
$iterator = new RecursiveArrayIterator($info);
iterator_apply($iterator, 'countDistinct', array($iterator, $values));

function countDistinct($iterator, $values) {
    while ( $iterator -> valid() ) {
        if ( $iterator -> hasChildren() ) {
            countDistinct($iterator -> getChildren(), $values);
        } else {
            if (!$values->offsetExists($iterator->key())) {
                $values->offsetSet($iterator->key(), new ArrayObject());
            }

            if (!$values->offsetGet($iterator->key())->offsetExists($iterator->current())) {
                $values->offsetGet($iterator->key())
                    ->offsetSet($iterator->current(), 1);
            } else {
                $values->offsetGet($iterator->key())
                    ->offsetSet($iterator->current(),
                $values->offsetGet($iterator->key())->offsetGet($iterator->current()) + 1);
            }
        }

        $iterator -> next();
    }
}

Sure, with this example you do not avoid the loop. But with the ArrayObject and the RecursiveArrayIterator you will have some memory and performance advantages.

The result of this will exactly match your expected result, which you can easyliy iterate with the getIterator() function of the ArrayObject.

Upvotes: 2

Guns
Guns

Reputation: 2728

Here is what might help you:

$returnArray = array('car' => NULL, 'previous_car' => NULL);

foreach($info as $newInfo) {
    $returnArray['car'][] = $newInfo['car'];
    $returnArray['previous_car'][] = $newInfo['previous_car'];
}

$ret['car'] = array_count_values($returnArray['car']);
$ret['previous_car'] = array_count_values($returnArray['previous_car']);

var_dump($ret);

This returns:

array (size=2)
  'car' => 
    array (size=3)
      'Audi' => int 3
      'BMW' => int 1
      'Ford' => int 1
  'previous_car' => 
    array (size=2)
      'BMW' => int 3
      'Seat' => int 2

Upvotes: 0

Mark Baker
Mark Baker

Reputation: 212522

If you're running PHP 5.5, you can use:

$newArray = array(
    'car' => array_count_values(array_column($info, 'car')),
    'previous_car' => array_count_values(array_column($info, 'previous_car'))
);
var_dump($newArray);

For versions of PHP prior to 5.5

$newArray = array(
    'car' => array_count_values(
        array_map(
            function($value) {
                return $value['car'];
            },
            $info
        )
    ),
    'previous_car' => array_count_values(
        array_map(
            function($value) {
                return $value['previous_car'];
            },
            $info
        )
    )
);
var_dump($newArray);

Upvotes: 4

smg628
smg628

Reputation: 179

You can write a function that will sort your data but for now check this out: http://www.php.net/manual/en/function.array-multisort.php

Upvotes: -1

Related Questions