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

420

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

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


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

6
以下是用于实现此功能的帮助函数:
/**
 * Helper function to rename array keys.
 */
function _rename_arr_key($oldkey, $newkey, array &$arr) {
    if (array_key_exists($oldkey, $arr)) {
        $arr[$newkey] = $arr[$oldkey];
        unset($arr[$oldkey]);
        return TRUE;
    } else {
        return FALSE;
    }
}

这篇文章基于@KernelM的回答

用法:

_rename_arr_key('oldkey', 'newkey', $my_array);

在成功重命名时,它将返回true,否则返回false


1
请注意,这会改变数组的顺序(重命名键的元素将位于数组末尾,而不是原本的位置)。此外,我通常不会以下划线开头的方式来命名函数名(传统上用于指定特殊的内部使用函数)。 - orrd

4
这段代码将有助于将旧密钥更改为新密钥。
$i = 0;
$keys_array=array("0"=>"one","1"=>"two");

$keys = array_keys($keys_array);

for($i=0;$i<count($keys);$i++) {
    $keys_array[$keys_array[$i]]=$keys_array[$i];
    unset($keys_array[$i]);
}
print_r($keys_array);

展示类似于
$keys_array=array("one"=>"one","two"=>"two");

...但是提问者并不是在寻找一种将值应用为新键的方法。如果这是任务,那么PHP已经本地提供了array_combine($keys_array, $keys_array)。实际上,我想说的是,即使你的片段提供了所需的效果,也不应该使用它。 - mickmackusa

4

简单的内容:

此函数将接受目标$hash,$replacements也是一个哈希,包含新键=>旧键的关联。

此函数将保留原始顺序,但对于非常大的(如10k条以上)数组可能存在问题,涉及到性能和内存方面的问题。

function keyRename(array $hash, array $replacements) {
    $new=array();
    foreach($hash as $k=>$v)
    {
        if($ok=array_search($k,$replacements))
            $k=$ok;
        $new[$k]=$v;
    }
    return $new;    
}

这种替代函数能够以更好的性能和内存使用效率完成相同的操作,但会失去原始顺序(但这不应该是问题,因为它是哈希表!)

function keyRename(array $hash, array $replacements) {

    foreach($hash as $k=>$v)
        if($ok=array_search($k,$replacements))
        {
          $hash[$ok]=$v;
          unset($hash[$k]);
        }

    return $hash;       
}

在使用array_search()时,不明确检查false是有问题的。如果搜索的键是第一个元素怎么办?我担心你正在教授不可靠的做法。此外,“loosing”的正确拼写应该是“losing”。 - mickmackusa
此外,假设提问者需要翻译其原始数组中的所有键,则您建议循环调用自定义函数,该函数进行循环函数调用而不使用早期的 return/break。这绝对不会表现良好。 - mickmackusa
修复丢失问题 - 如果(($ok=array_search($k,$replacements)!==false)将更安全。 - Nadir

3

因为问题本身没有最小可验证示例,所以本页面充斥着广泛的解释。一些答案仅仅试图解决“标题”而不费心理解问题的要求。

关键字实际上是一个ID号码,值是一个计数。这对于大多数情况来说都很好,但我想要一个函数,它能够获取数组的可读名称,并将其用作关键字,而不改变值。

PHP关键字无法被更改,但可以被替换——这就是为什么许多答案建议使用array_search()(性能相对较差)和unset()的原因。

最终,您需要创建一个新数组,在原始计数相关的名称中使用关键字。这通过查找数组最有效地完成,因为查找关键字总是优于查找值。

代码: (Demo)

$idCounts = [
    3 => 15,
    7 => 12,
    8 => 10,
    9 => 4
];

$idNames = [
    1 => 'Steve',
    2 => 'Georgia',
    3 => 'Elon',
    4 => 'Fiona',
    5 => 'Tim',
    6 => 'Petra',
    7 => 'Quentin',
    8 => 'Raymond',
    9 => 'Barb'
];

$result = [];
foreach ($idCounts as $id => $count) {
    if (isset($idNames[$id])) {
        $result[$idNames[$id]] = $count;
    }
}
var_export($result);

输出:

array (
  'Elon' => 15,
  'Quentin' => 12,
  'Raymond' => 10,
  'Barb' => 4,
)

这种技术维护了原始数组顺序(如果排序很重要),不进行任何不必要的迭代,并且由于isset()的存在,将非常迅速。


2
您可以基于array_walk函数使用此功能:
function mapToIDs($array, $id_field_name = 'id')
{
    $result = [];
    array_walk($array, 
        function(&$value, $key) use (&$result, $id_field_name)
        {
            $result[$value[$id_field_name]] = $value;
        }
    );
    return $result;
}

$arr = [0 => ['id' => 'one', 'fruit' => 'apple'], 1 => ['id' => 'two', 'fruit' => 'banana']];
print_r($arr);
print_r(mapToIDs($arr));

它给出:

Array(
    [0] => Array(
        [id] => one
        [fruit] => apple
    )
    [1] => Array(
        [id] => two
        [fruit] => banana
    )
)

Array(
    [one] => Array(
        [id] => one
        [fruit] => apple
    )
    [two] => Array(
        [id] => two
        [fruit] => banana
    )
)

这个回答(完全忽略了提问者描述的场景)是过度工程化的解决方案,而 PHP 已经为此提供了本地函数调用。我永远不会在我的任何代码中使用你的脚本。我会使用这个:array_column($arr, null, 'id')。但再次强调,我必须说你的答案与这个问题无关,这个问题只处理平面数组。 - mickmackusa

2

如果您想一次替换多个键(保留顺序):

/**
 * Rename keys of an array
 * @param array $array (asoc)
 * @param array $replacement_keys (indexed)
 * @return array
 */
function rename_keys($array, $replacement_keys)  {
      return array_combine($replacement_keys, array_values($array));
}

使用方法:

$myarr = array("a" => 22, "b" => 144, "c" => 43);
$newkeys = array("x","y","z");
print_r(rename_keys($myarr, $newkeys));
//must return: array("x" => 22, "y" => 144, "z" => 43);

array_combine()不需要其参数为索引数组,因此不需要使用array_values()。 https://3v4l.org/uN1ZF 这个答案依赖于第二个数组已经与第一个数组中的ID完全对齐。这在实际项目中很少发生,对于研究人员来说不太可能是可靠的解决方案。 - mickmackusa
1
@mickmackusa,我认为你是对的。我猜这里的问题是问题对我来说不太清楚。现在重新阅读这个问题(6年后),我相信你的答案解决了它(无论如何...我不再使用PHP了...呵呵)。 - lepe

2

此函数将通过与索引搜索相结合,重命名数组键名并保留其位置。

function renameArrKey($arr, $oldKey, $newKey){
    if(!isset($arr[$oldKey])) return $arr; // Failsafe
    $keys = array_keys($arr);
    $keys[array_search($oldKey, $keys)] = $newKey;
    $newArr = array_combine($keys, $arr);
    return $newArr;
}

使用方法:

$arr = renameArrKey($arr, 'old_key', 'new_key');

这看起来像是为了重新映射数组键而进行的大量繁重工作。 - mickmackusa

2

这个基本函数处理交换数组键并保持数组原始顺序...

public function keySwap(array $resource, array $keys)
{
    $newResource = [];

    foreach($resource as $k => $r){
        if(array_key_exists($k,$keys)){
            $newResource[$keys[$k]] = $r;
        }else{
            $newResource[$k] = $r;
        }
    }

    return $newResource;
}

例如,你可以循环遍历并将所有的“a”键与“z”键交换...

$inputs = [
  0 => ['a'=>'1','b'=>'2'],
  1 => ['a'=>'3','b'=>'4']
]

$keySwap = ['a'=>'z'];

foreach($inputs as $k=>$i){
    $inputs[$k] = $this->keySwap($i,$keySwap);
}

所以,你真的建议问答者使用循环调用自定义函数并进行完整数组循环?而且没有 break 吗?这不是一个高效的解决方案。 - mickmackusa

1
这适用于重命名第一个键:
$a = ['catine' => 'cat', 'canine'  => 'dog'];
$tmpa['feline'] = $a['catine'];
unset($a['catine']);
$a = $tmpa + $a;

然后,print_r($a)显示一个已修复的顺序数组:
Array
(
    [feline] => cat
    [canine] => dog
)

这适用于重命名任意键:

$a = ['canine'  => 'dog', 'catine' => 'cat', 'porcine' => 'pig']
$af = array_flip($a)
$af['cat'] = 'feline';
$a = array_flip($af)

print_r($a)

Array
(
    [canine] => dog
    [feline] => cat
    [porcine] => pig
)

一个通用函数:
function renameKey($oldkey, $newkey, $array) {
    $val = $array[$oldkey];
    $tmp_A = array_flip($array);
    $tmp_A[$val] = $newkey;

    return array_flip($tmp_A);
}

如果值可能包含重复项,则翻转数组不是一个稳定的解决方案。所提出的问题说明输入数据包含ID作为键和计数作为值。这些计数很可能包含重复项。因此,翻转是一个不好的选择。您之前编写的unset&union过于硬编码。问答者不太可能对整个数组进行单独/手动编码元素声明。 - mickmackusa

1
当使用完整数组时,有一种替代方法可以更改数组元素的键,而不改变数组的顺序。这就是将数组复制到一个新数组中。例如,我正在使用一个混合的多维数组,其中包含索引和关联键,并且我想用它们的值替换整数键,而不破坏顺序。我通过交换所有数字数组条目的键/值来实现这一点 - 在这里:['0' => 'foo']。请注意,顺序保持不变。
<?php
$arr = [
    'foo',
    'bar'=>'alfa',
    'baz'=>['a'=>'hello', 'b'=>'world'],
];

foreach($arr as $k=>$v) {
    $kk = is_numeric($k) ? $v : $k;
    $vv = is_numeric($k) ? null : $v;
    $arr2[$kk] = $vv;
}

print_r($arr2);

输出:

Array (
    [foo] => 
    [bar] => alfa
    [baz] => Array (
            [a] => hello
            [b] => world
        )
)

这个回答如何回答原问题?看起来这个偏离已经不再尊重被问的内容了。 - mickmackusa

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