Reputation: 467
I have a multidimensional array, in which I want to count similar occurrences.
So basically I want this:
[
[
'type' => 'frosties',
'madeby' => 'kelloggs'
],
[
'type' => 'frosties',
'madeby' => 'kelloggs'
],
[
'type' => 'cornflakes',
'madeby' => 'kelloggs'
]
];
To end out as this:
[
[
'type' => 'frosties',
'madeby' => 'kelloggs',
'count' => 2
],
[
'type' => 'cornflakes',
'madeby' => 'kelloggs',
'count' => 1
]
]
This is what I've come up with so far:
public function count($array) {
$newArr = [];
foreach ($array as $breakfast) {
if (in_array($breakfast['type'], $newArr) && in_array($breakfast['madeby'], $newArr)) {
//what goes here?
//dosomething['count']++;
} else {
$newArr[] = [
'type' => $breakfast['type'],
'madeby' => $breakfast['madeby'],
'count' => 0
];
}
}
return $newArr;
}
I might have been staring at this for too long, but I just can't seem to come up with what goes inside the if().
Upvotes: 3
Views: 1277
Reputation: 22353
To save some memory and make the look up faster, I'd suggest to use an implementation that makes use of the \ArrayAccess
interface. The CerialStack
keeps two internal arrays: One to hold the stack where we append and another smaller one that just serves a purpose as look up map.
class CerialStack implements \ArrayAccess
{
/** @var array Container */
private $stack = [];
/** @var array Flat map of `type` for look ups */
private $map = [];
// Only look up the map
public function offsetExists( $type )
{
return in_array( $type, $this->map );
}
// Only looks up the map
public function offsetGet( $type )
{
return $this->offsetExists( $type )
? $this->stack[ array_search( $type, $this->map ) ]
: false;
}
// Sets both the map as well as the stack (if the map look up return false)
// increases the counter if the value exists
public function offsetSet( $index, $value )
{
$type = $value['type'];
if ( ! $this->offsetGet( $type ) )
{
$this->map[] = $type;
$this->stack[] = array_merge( [ 'count' => 1, ], $value );
}
else
{
$key = $this->getKey( $type );
$key and $this->stack[ $key ]['count']++;
}
}
// reduces both the map and the stack
public function offsetUnset( $type )
{
$key = $this->getKey( $type );
if ( $key )
unset(
$this->stack[ $key ],
$this->map[ $key ]
);
}
private function getKey( $type )
{
return array_search( $type, $this->map );
}
public function getStack()
{
return $this->stack;
}
}
The Stack class allows pretty fast look ups as well as handling the array as you are used to. No magic or special function calls involved.
To sort the stack, I'd suggest to use a \SplMaxHeap
implementation as I've written in another answer. Just alter the class slightly and replace ->rating
with ['count']
in the custom heap.
Let's test that:
// Test data
$cerials = [
[
'type' => 'frosties',
'madeby' => 'kelloggs',
],
[
'type' => 'frosties',
'madeby' => 'kelloggs',
],
[
'madeby' => 'kelloggs',
'type' => 'cornflakes',
]
];
$it = new \CerialStack;
// Push into stack
foreach ( $cerials as $cerial )
$it[] = $cerial;
// Dump the stack
var_dump( $it->getStack() );
// Output
array (size=2)
0 =>
array (size=3)
'count' => int 2
'type' => string 'frosties' (length=8)
'madeby' => string 'kelloggs' (length=8)
1 =>
array (size=3)
'count' => int 1
'madeby' => string 'kelloggs' (length=8)
'type' => string 'cornflakes' (length=10)
Upvotes: 0
Reputation: 4025
Here you go:
$array = [
[
'type' => 'frosties',
'madeby' => 'kelloggs'
],
[
'type' => 'frosties',
'madeby' => 'kelloggs'
],
[
'type' => 'cornflakes',
'madeby' => 'kelloggs'
]
];
$results = [];
foreach ($array as $pair) {
//ksort($pair); <- might need ksort here if type and madeby are not in the same order.
$key = serialize($pair);
if (isset($results[$key])) {
$results[$key]['count']++;
} else {
$results[$key] = array_merge($pair, ['count' => 1]);
}
}
$results = array_values($results);
print_r($results);
Upvotes: 3
Reputation: 874
Try this and change function name to something else:
<?php
$array = array(
array (
'type' => 'frosties',
'madeby' => 'kelloggs',
),
array (
'type' => 'frosties',
'madeby' => 'kelloggs'
),
array(
'type' => 'cornflakes',
'madeby' => 'kelloggs'
)
);
print_r(counta($array));
function counta($array) {
$newArr = [];
foreach ($array as $breakfast) {
if(empty($newArr))
{
$newArr[] = array (
'type' => $breakfast['type'],
'madeby' => $breakfast['madeby'],
'count' => 1
);
}
else
{
foreach($newArr as $k=>$tmp)
{
if($tmp['type']==$breakfast['type'] && $tmp['madeby']==$breakfast['madeby'])
{
$newArr[$k]['count']=$newArr[$k]['count']+1;
}
else{
$newArr[] = array (
'type' => $breakfast['type'],
'madeby' => $breakfast['madeby'],
'count' => 1
);
}
}
}
}
return $newArr;
}
?>
Upvotes: -1