PHP多维数组:用两个值的串联替换所有键

时间:2021-12-25 10:44:57

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":

我在PHP中有一个多维数组,其中外部数组包含数千个项目,其中每个项目都是一个数组本身,其值为“key1”,“key2”和“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:

我想将它转换为一个非常简单的数组,其中来自“key1”和“key”的前值被连接成一个新的键,指向相应的“count”值,如下所示:

  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:

性能对我来说非常重要,因为外部阵列有几千个项目。 PHP中有快速方法吗?我得到的唯一的东西是一个简单的迭代,但这对我来说似乎很慢:

// 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.

有些人正确地补充说我当前的解决方案已经在O(n)中并且提到PHP中没有内置函数来加速这个。

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:

我从mysql数据库查询中获取“myExistingArray”。我基本上有工作对象,并希望按状态和event_id对它们进行分组。查询与此类似:

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.

我想重新排列密钥,以便稍后我可以轻松访问具有特定状态的特定事件的作业计数。

4 个解决方案

#1


2  

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.

通常,您正在寻找array_walk或者array_map函数来转换PHP中的数组,但不幸的是它们都不能改变您想要转换的数组的键。 array_walk将保留密钥,但不会改变它们。可悲的是,不,没有内置的功能来做你所要求的。

#2


1  

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.

在合并的数组上执行var_dump()会减慢速度(如预期的那样),但是如果保留内存,数据也不会太糟糕。

And the PHP used to test:

PHP用于测试:

// 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.

编辑:为Oldskool示例添加了测试。

#3


0  

You could change your foreach to only iterate over the keys and not the entire sub-arrays, by changing it to:

您可以将foreach更改为仅迭代键而不是整个子阵列,方法是将其更改为:

foreach (array_keys($myExistingArray) as $item) {
    $myNewArray[$myExistingArray[$item]['key1'] . '_' . $myExistingArray[$item]['key2']] = $myExistingArray[$item]['count'];
}

This will gain you some slight speed advantage (see comparison of the times here (array_keys method) and here (your original method)). On very large arrays, the difference will likely become more noticable.

这将获得一些轻微的速度优势(请参阅此处的比较(array_keys方法)和此处(您的原始方法))。在非常大的阵列上,差异可能会变得更加明显。

#4


0  

If speed is the issue, and you are not using the final array as a map, I would create a generator, so that you don't have to precalculate everything.

如果速度是问题,并且您没有使用最终数组作为地图,我会创建一个生成器,这样您就不必预先计算所有内容。

$myExistingArray = [ ... ];
class MyNewArrayIterator implements IteratorAggregate {
    protected $array;
    public function __construct(array $array) {
        $this->array = $array;
    }
    public function getIterator() {
        foreach ($this->array as $value) {
            yield $value['key1'] . '_' . $value['key2'] => $value['count'];
        }
    }
}

And then you can do:

然后你可以这样做:

$myNewArray = new MyNewArrayIterator($myExistingArray);
foreach($myNewArray as $key => $value) {
    echo $key . ": " . $value;
}

This may or may not be useful in your use case.

这在您的用例中可能有用,也可能没用。

#1


2  

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.

通常,您正在寻找array_walk或者array_map函数来转换PHP中的数组,但不幸的是它们都不能改变您想要转换的数组的键。 array_walk将保留密钥,但不会改变它们。可悲的是,不,没有内置的功能来做你所要求的。

#2


1  

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.

在合并的数组上执行var_dump()会减慢速度(如预期的那样),但是如果保留内存,数据也不会太糟糕。

And the PHP used to test:

PHP用于测试:

// 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.

编辑:为Oldskool示例添加了测试。

#3


0  

You could change your foreach to only iterate over the keys and not the entire sub-arrays, by changing it to:

您可以将foreach更改为仅迭代键而不是整个子阵列,方法是将其更改为:

foreach (array_keys($myExistingArray) as $item) {
    $myNewArray[$myExistingArray[$item]['key1'] . '_' . $myExistingArray[$item]['key2']] = $myExistingArray[$item]['count'];
}

This will gain you some slight speed advantage (see comparison of the times here (array_keys method) and here (your original method)). On very large arrays, the difference will likely become more noticable.

这将获得一些轻微的速度优势(请参阅此处的比较(array_keys方法)和此处(您的原始方法))。在非常大的阵列上,差异可能会变得更加明显。

#4


0  

If speed is the issue, and you are not using the final array as a map, I would create a generator, so that you don't have to precalculate everything.

如果速度是问题,并且您没有使用最终数组作为地图,我会创建一个生成器,这样您就不必预先计算所有内容。

$myExistingArray = [ ... ];
class MyNewArrayIterator implements IteratorAggregate {
    protected $array;
    public function __construct(array $array) {
        $this->array = $array;
    }
    public function getIterator() {
        foreach ($this->array as $value) {
            yield $value['key1'] . '_' . $value['key2'] => $value['count'];
        }
    }
}

And then you can do:

然后你可以这样做:

$myNewArray = new MyNewArrayIterator($myExistingArray);
foreach($myNewArray as $key => $value) {
    echo $key . ": " . $value;
}

This may or may not be useful in your use case.

这在您的用例中可能有用,也可能没用。