Pergunta

Eu tenho uma matriz associativa no formulário key => value Onde a chave é um valor numérico, no entanto, não é um valor numérico seqüencial. A chave é na verdade um número de identificação e o valor é uma contagem. Isso é bom para a maioria das instâncias, no entanto, quero uma função que obtenha o nome legível por humanos da matriz e use isso para a chave, sem alterar o valor.

Não vi uma função que faça isso, mas suponho que preciso fornecer a chave antiga e a nova chave (as quais tenho) e transformar a matriz. Existe uma maneira eficiente de fazer isso?

Foi útil?

Solução

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

Outras dicas

A maneira como você faria isso e preservar a ordem da matriz é colocando as teclas da matriz em uma matriz separada, encontre e substitua a chave nessa matriz e combine -a novamente com os valores.

Aqui está uma função que faz exatamente isso:

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 );
}

se seu array é construído a partir de uma consulta de banco de dados, você pode alterar a chave diretamente do mysql declaração:

ao invés de

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

Use algo como:

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

A resposta do kernelm é boa, mas para evitar a questão levantada por Greg no comentário (chaves conflitantes), usar uma nova matriz seria mais seguro

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

Você pode usar uma segunda matriz associativa que mapeia nomes legíveis humanos para os IDs. Isso também proporcionaria um relacionamento de muitos a 1. Então faça algo assim:

echo 'Widgets: ' . $data[$humanreadbleMapping['Widgets']];

Se você deseja também a posição da nova chave da matriz para ser a mesma que a antiga, você pode fazer isso:

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;
}

If your array is recursive you can use this function: test this data:

    $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,
            )
    );

here is the function:

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;   
}
$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
];
*/

I like KernelM's solution, but I needed something that would handle potential key conflicts (where a new key may match an existing key). Here is what I came up with:

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;
    }
}

You can then cycle through an array like this:

$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' )

Here is a helper function to achieve that:

/**
 * 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;
    }
}

pretty based on @KernelM answer.

Usage:

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

It will return true on successful rename, otherwise false.

Easy stuff:

this function will accept the target $hash and $replacements is also a hash containing newkey=>oldkey associations.

This function will preserve original order, but could be problematic for very large (like above 10k records) arrays regarding performance & memory.

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;    
}

this alternative function would do the same, with far better performance & memory usage, at the cost of loosing original order (which should not be a problem since it is hashtable!)

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;       
}

this code will help to change the oldkey to new one

$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);

display like

$keys_array=array("one"=>"one","two"=>"two");

this works for renaming the first key:

$a = ['catine' => 'cat', 'canine'  => 'dog'];
$tmpa['feline'] = $a['catine'];
unset($a['catine']);
$a = $tmpa + $a;

then, print_r($a) renders a repaired in-order array:

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

this works for renaming an arbitrary key:

$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
)

a generalized function:

function renameKey($oldkey, $newkey, $array) {
    $val = $array[$oldkey];
    $tmp_A = array_flip($array);
    $tmp_A[$val] = $newkey;

    return array_flip($tmp_A);
}

If you want to replace several keys at once (preserving order):

/**
 * 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));
}

Usage:

$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);

There is an alternative way to change the key of an array element when working with a full array - without changing the order of the array. It's simply to copy the array into a new array.

For instance, I was working with a mixed, multi-dimensional array that contained indexed and associative keys - and I wanted to replace the integer keys with their values, without breaking the order.

I did so by switching key/value for all numeric array entries - here: ['0'=>'foo']. Note that the order is intact.

<?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);

Output:

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

You can use this function based on 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));

It gives:

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
    )
)

Hmm, I'm not test before, but I think this code working

function replace_array_key($data) {
    $mapping = [
        'old_key_1' => 'new_key_1',
        'old_key_2' => 'new_key_2',
    ];

    $data = json_encode($data);
    foreach ($mapping as $needed => $replace) {
        $data = str_replace('"'.$needed.'":', '"'.$replace.'":', $data);
    }

    return json_decode($data, true);
}

One which preservers ordering that's simple to understand:

function rename_array_key(array $array, $old_key, $new_key) {
  if (!array_key_exists($old_key, $array)) {
      return $array;
  }
  $new_array = [];
  foreach ($array as $key => $value) {
    $new_key = $old_key === $key
      ? $new_key
      : $key;
    $new_array[$new_key] = $value;
  }
  return $new_array;
}

best way is using reference, and not using unset (which make another step to clean memory)

$tab = ['two' => [] ];

solution:

$tab['newname'] = & $tab['two'];

you have one original and one reference with new name.

or if you don't want have two names in one value is good make another tab and foreach on reference

foreach($tab as $key=> & $value) {
    if($key=='two') { 
        $newtab["newname"] = & $tab[$key];
     } else {
        $newtab[$key] = & $tab[$key];
     }
}

Iterration is better on keys than clone all array, and cleaning old array if you have long data like 100 rows +++ etc..

You can write simple function that applies the callback to the keys of the given array. Similar to array_map

<?php
function array_map_keys(callable $callback, array $array) {
    return array_merge([], ...array_map(
        function ($key, $value) use ($callback) { return [$callback($key) => $value]; },
        array_keys($array),
        $array
    ));
}

$array = ['a' => 1, 'b' => 'test', 'c' => ['x' => 1, 'y' => 2]];
$newArray = array_map_keys(function($key) { return 'new' . ucfirst($key); }, $array);

echo json_encode($array); // {"a":1,"b":"test","c":{"x":1,"y":2}}
echo json_encode($newArray); // {"newA":1,"newB":"test","newC":{"x":1,"y":2}}

Here is a gist https://gist.github.com/vardius/650367e15abfb58bcd72ca47eff096ca#file-array_map_keys-php.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top