Reputation: 22
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
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
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
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
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
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