根据另一个查找/映射数组替换数组中的键

420

我有一个形式为key => value的关联数组,其中键是数字值,但它不是顺序数字值。实际上,键是一个ID号码,而值是一个计数。这在大多数情况下都很好,但是我想要一个函数,该函数获取数组的可读名称并将其用作键,而不更改值。

我没有看到可以完成此任务的函数,但我假设我需要提供旧键和新键(我都有),并转换数组。有没有一种有效的方法来实现这个?


请参考以下链接:https://dev59.com/onVC5IYBdhLWcg3wZwJT - Peter Krauss
25个回答

666
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);

7
请注意以下两点:1)没有两个密钥的人类可读版本相同;2)不要出现人类可读版本是数字的情况。 - Greg
96
这可能会改变数组的顺序,你需要小心。即使在PHP中使用关联数组时,它们也是有顺序的,有时这个顺序是被利用的。 - Robin Winslow
8
是的,罗宾说得很好。有没有一种方法保持相同的顺序?还是需要创建一个新数组才能实现? - Simon East
45
奖励问题:如何更改ID,但保留数组顺序? - Petr Peller
20
如果关键值不发生改变,您将删除一个数组元素。您可能需要检查一下。 - Peeech
显示剩余6条评论

120

如果想保留数组的顺序并进行键名替换,可以将数组的键名放入一个单独的数组中,找到并替换该数组中的键名,然后再将其与值结合起来。

以下是可以实现此操作的函数:

function change_key( $array, $old_key, $new_key ) {

    if( ! array_key_exists( $old_key, $array ) )
        return $array;

    $keys = array_keys( $array );
    $keys[ array_search( $old_key, $keys ) ] = $new_key;

    return array_combine( $keys, $array );
}

3
谢谢,这真的很有帮助,因为我需要保留数组的顺序。在找到这个页面之前,我已经尝试过被接受的答案了。 - gillytech
4
非常希望保留数组的顺序,看起来更整洁。 - Phil Cook
如果您在意性能或顺序保留,请注意:https://dev59.com/RXVC5IYBdhLWcg3wnCaA#58619985 - Léo Benoist
1
这是非常糟糕的实现,从性能角度来看。最好单独保留顺序,或者像这个例子中使用array_splice:https://dev59.com/b2865IYBdhLWcg3wd-ce - Milos Radojevic

60
如果你的数组是从数据库查询中构建出来的,你可以直接在mysql语句中改变key:

不要使用:

"select ´id´ from ´tablename´..."

可以使用类似以下的方式:

"select ´id´ **as NEWNAME** from ´tablename´..."

1
与其回避这个任务并假设输入数据来自结果集,也许你在发布答案之前应该寻求澄清。 - mickmackusa
1
@Conrado,我理解这个答案满足了你的个人需求,但是在 Stack Overflow 页面上添加假设情况会拖慢研究者的体验,因此不适合扩展。请阅读我的答案,了解为什么这个页面上的许多答案“未能达到”实际问题要求。 - mickmackusa

22

KernelM的答案很好,但为了避免Greg在评论中提出的问题(冲突键),使用一个新数组会更安全。

$newarr[$newkey] = $oldarr[$oldkey];
$oldarr=$newarr;
unset($newarr);

这是一个很好的解决方案,只要你的数组大小合理。如果你的数组占用了超过可用PHP内存一半以上的空间,那么这个方法就行不通了。 - kingjeffrey
14
@kingjeffrey,并不完全是这样。只要"只是复制"数组的值而没有修改它们,那么这些值就不会被复制。例如,如果有一个包含10,000个元素且占用40MB内存的数组,复制它将消耗存储10,000个已存在值的引用的内存,而不是存储值的副本,因此,如果1个数组使用40MB,则它的副本可能只会消耗大概0.5MB(经过测试)。 - binaryLV

19
$array = [
    'old1' => 1
    'old2' => 2
];

$renameMap = [
    'old1' => 'new1',   
    'old2' => 'new2'
];

$array = array_combine(array_map(function($el) use ($renameMap) {
    return $renameMap[$el];
}, array_keys($array)), array_values($array));

/*
$array = [
    'new1' => 1
    'new2' => 2
];
*/

2
紧凑而简洁。如果您正在处理一个大数组并且不想更改所有键,则映射函数中的行变为return isset($renameMap[$el]) ? $renameMap[$el] : $el; - Jerry
1
这个答案缺少教育性的解释。 - mickmackusa

17
你可以使用第二个关联数组来将可读的名称映射为ID,这也提供了多对一的关系。然后可以这样做:
echo 'Widgets: ' . $data[$humanreadbleMapping['Widgets']];

15

两种解决方案的简单基准比较。

解决方案1: 复制并删除(顺序丢失,但更快)https://dev59.com/RXVC5IYBdhLWcg3wnCaA#240676

<?php
$array = ['test' => 'value', ['etc...']];

$array['test2'] = $array['test'];
unset($array['test']);

解决方案2:重命名键https://dev59.com/RXVC5IYBdhLWcg3wnCaA#21299719

<?php
$array = ['test' => 'value', ['etc...']];

$keys = array_keys( $array );
$keys[array_search('test', $keys, true)] = 'test2';
array_combine( $keys, $array );

基准测试:

<?php
$array = ['test' => 'value', ['etc...']];


for ($i =0; $i < 100000000; $i++){
    // Solution 1
}


for ($i =0; $i < 100000000; $i++){
    // Solution 2
}

结果:

php solution1.php  6.33s  user 0.02s system 99% cpu 6.356  total
php solution1.php  6.37s  user 0.01s system 99% cpu 6.390  total
php solution2.php  12.14s user 0.01s system 99% cpu 12.164 total
php solution2.php  12.57s user 0.03s system 99% cpu 12.612 total

你的基准测试似乎不正确。你只是在改变第一个元素一亿次,之后没有进行任何更改。我认为,如果基准测试更改多个键,则第二个解决方案将更快,因为所有操作都在 C++ 端完成,中间不需要返回 PHP。 - AaA

13

如果你想让新数组键的位置与旧键相同,你可以这样做:

function change_array_key( $array, $old_key, $new_key) {
    if(!is_array($array)){ print 'You must enter a array as a haystack!'; exit; }
    if(!array_key_exists($old_key, $array)){
        return $array;
    }

    $key_pos = array_search($old_key, array_keys($array));
    $arr_before = array_slice($array, 0, $key_pos);
    $arr_after = array_slice($array, $key_pos + 1);
    $arr_renamed = array($new_key => $array[$old_key]);

    return $arr_before + $arr_renamed + $arr_after;
}

7
如果您的数组是递归的,您可以使用此函数: 测试这个数据:
    $datos = array
    (
        '0' => array
            (
                'no' => 1,
                'id_maquina' => 1,
                'id_transaccion' => 1276316093,
                'ultimo_cambio' => 'asdfsaf',
                'fecha_ultimo_mantenimiento' => 1275804000,
                'mecanico_ultimo_mantenimiento' =>'asdfas',
                'fecha_ultima_reparacion' => 1275804000,
                'mecanico_ultima_reparacion' => 'sadfasf',
                'fecha_siguiente_mantenimiento' => 1275804000,
                'fecha_ultima_falla' => 0,
                'total_fallas' => 0,
            ),

        '1' => array
            (
                'no' => 2,
                'id_maquina' => 2,
                'id_transaccion' => 1276494575,
                'ultimo_cambio' => 'xx',
                'fecha_ultimo_mantenimiento' => 1275372000,
                'mecanico_ultimo_mantenimiento' => 'xx',
                'fecha_ultima_reparacion' => 1275458400,
                'mecanico_ultima_reparacion' => 'xx',
                'fecha_siguiente_mantenimiento' => 1275372000,
                'fecha_ultima_falla' => 0,
                'total_fallas' => 0,
            )
    );

这是函数:

function changekeyname($array, $newkey, $oldkey)
{
   foreach ($array as $key => $value) 
   {
      if (is_array($value))
         $array[$key] = changekeyname($value,$newkey,$oldkey);
      else
        {
             $array[$newkey] =  $array[$oldkey];    
        }

   }
   unset($array[$oldkey]);          
   return $array;   
}

6

我喜欢KernelM的解决方案,但我需要处理潜在的键冲突(新键可能与现有键匹配)。这是我想出来的解决方案:

function swapKeys( &$arr, $origKey, $newKey, &$pendingKeys ) {
    if( !isset( $arr[$newKey] ) ) {
        $arr[$newKey] = $arr[$origKey];
        unset( $arr[$origKey] );
        if( isset( $pendingKeys[$origKey] ) ) {
            // recursion to handle conflicting keys with conflicting keys
            swapKeys( $arr, $pendingKeys[$origKey], $origKey, $pendingKeys );
            unset( $pendingKeys[$origKey] );
        }
    } elseif( $newKey != $origKey ) {
        $pendingKeys[$newKey] = $origKey;
    }
}

您可以按照以下方式循环遍历数组:
$myArray = array( '1970-01-01 00:00:01', '1970-01-01 00:01:00' );
$pendingKeys = array();
foreach( $myArray as $key => $myArrayValue ) {
    // NOTE: strtotime( '1970-01-01 00:00:01' ) = 1 (a conflicting key)
    $timestamp = strtotime( $myArrayValue );
    swapKeys( $myArray, $key, $timestamp, $pendingKeys );
}
// RESULT: $myArray == array( 1=>'1970-01-01 00:00:01', 60=>'1970-01-01 00:01:00' )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接