Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP multidimensional array: replace all keys with concatenation of two values

I have a multidimensional array in PHP, where the outer array contains several thousands items and each item inside is an array itself with the values "key1", "key2" and "count":

 myExistingArray (size=99999 VERY BIG)
      public 0 => 
        array (size=3)
          'key1' => string '15504' 
          'key2' => string '20'
          'count' => string '1'
      public 1 => 
        array (size=3)
          'key1' => string '15508' (length=5)
          'key2' => string '20' (length=2)
          'count' => string '2' (length=1)
      public 2 => 
        array (size=3)
          'key1' => string '15510' (length=5)
          'key2' => string '20' (length=2)
          'count' => string '5' (length=1)
....many more similar items

I want to transform this into a very simple array, where the former values from "key1" and "key" are concatenated to be a new key that points to the corressponding "count" value like so:

  myNewArray (size=99999 VERY BIG)
      <key1>_<key2> => <count>
      15504_20 => string '1' (length=1)
      15508_20 => string '2' (length=1)
      15510_20 => string '5' (length=1)

Performance is very important for me since the outer array has several thousand items. Is there a fast method in PHP? The only thing I got was a simple iteration, but this seems to slow for me:

// works but I am looking for a faster version
$myNewArray = array();
foreach ($myExistingArray as $item) {
  $myNewArray [$item["key1"]."_".$item["key1"]]=$item["count"];
}

EDIT / Underlying problem

Some people rightfully added that my current solution is already in O(n) and mentioned that there is no built-in function in PHP to speed this up.

I get "myExistingArray" from a mysql database query. I basically have job objects and want to group them by their status and their event_id. The query similiar to this:

select count(job.id) as count, job.status as key1, job.event_id as key2
from job
group by job.status, job.event_id

I want to rearrange the keys so that later I can easily access the count of jobs for a certain event with a certain status.

like image 472
Pascal Klein Avatar asked Aug 17 '16 11:08

Pascal Klein


2 Answers

Ordinarily, you'd be looking for either the array_walk or maybe the array_map function to transform arrays in PHP, but unfortunately neither of them can alter the key of the array that you want to transform. array_walk will preserve the keys, but won't alter them. So sadly, no, there's no built in function to do what you're asking.

like image 107
Gareth Parker Avatar answered Oct 04 '22 01:10

Gareth Parker


Done a few test with the following results (almost all the same).

Test 1:  [0.25861501693726]
Test 2:  [0.20804476737976]
Test 3:  [0.21039199829102]
Oldskool:[0.26545000076294]
Test 4:  [0.35072898864746]

Doing a var_dump() on the merged array will slow things down (as expected), but if you keep it memory the data is not too bad to work with.

And the PHP used to test:

// Construct the raw data
$i = 0;
do {
    $raw[] = array('key1' => mt_rand(10000,99999), 'key2' => mt_rand(10,99), 'count' => $i);
} while(++$i < 100000);

// Test 1
$before = microtime(true);
foreach($raw as $k => $v) {
    $clean[$v['key1'].'_'.$v['key2']] = $v['count'];
}
$after = microtime(true);
echo 'Test 1:['.($after - $before).']<br />';

$clean = false;
$i = 0;

// Test 2
$before = microtime(true);
$max = count($raw);
do {
    $clean[$raw[$i]['key1'].'_'.$raw[$i]['key2']] = $raw[$i]['count'];
} while(++$i < $max);
$after = microtime(true);
echo 'Test 2:['.($after - $before).']<br />';

$clean = false;
$i = 0;

// Test 3
$before = microtime(true);
$max = count($raw);
for($i; $i < $max; $i++) {
    $clean[$raw[$i]['key1'].'_'.$raw[$i]['key2']] = $raw[$i]['count'];
}
$after = microtime(true);
echo 'Test 3:['.($after - $before).']<br />';

$clean = false;

// Test of Oldskool's suggestion
$before = microtime(true);
foreach (array_keys($raw) as $item) {
    $clean[$raw[$item]['key1'].'_'.$raw[$item]['key2']] = $raw[$item]['count'];
}
$after = microtime(true); 
echo 'Test Oldskool:['.($after - $before).']<br />';

$clean = false;
$i = 0;

// Test 4, just for fun
$before = microtime(true);
$max = count($raw);
do {
    $c = array_pop($raw[$i]);
    $clean[join('_', $raw[$i])] = $c;
} while(++$i < $max);
$after = microtime(true);
echo 'Test 4:['.($after - $before).']<br />';

Edit: Added a test for Oldskool example.

like image 45
Tigger Avatar answered Oct 04 '22 01:10

Tigger