Kirzilla
Kirzilla

Reputation: 16596

Fastest way to add a prefix to all array keys?

What is the fastest way to prepend a string to all keys in a flat array?

Input

$array = array(
 '1' => 'val1',
 '2' => 'val2',
);

Needed output:

$array = array(
  'prefix1' => 'val1',
  'prefix2' => 'val2',
);

Upvotes: 42

Views: 45545

Answers (10)

mickmackusa
mickmackusa

Reputation: 47904

Update 2024-03-28:

Very much to my surprise, using substr_replace() inside of array_combine() (slightly) outperforms foreach() in roughly half of the tests that I performed. This would be my preferred array key prefixer! It might not be intuitive, but it is concise, functional-style, doesn't declare temporary or result variables, and has no iterated user-defined functions. (See the full test results for different versions)

function substrReplaceAndCombinePrefix($array, $prefix = 'prefix') {
    return array_combine(
               substr_replace(array_keys($array), $prefix, 0, 0),
               $array
           );
}

Earlier answer:

What isn't surprising is that a classic foreach() loop is the best performer. It also happens to be the most concise script. If your code styling preference doesn't demand functional syntax, then this is probably the best approach. That said, if your input volume is significantly large, it will be a good idea to benchmark your actual data if performance is a high priority.

What is surprising (to me anyway), is that calling three specific native functions (array_combine(), array_map(), array_keys()) can outperform two specific native functions (array_reduce(), array_keys()) and even one native function (array_walk())!

array_walk() isn't horrifically slow, but array_reduce() suffers wicked performance drag.

The following benchmarks are the average of 20 calls of the technique upon arrays with 5000 elements. (Benchmark Script)

  1. foreach(): (PHP8.2.5 exec time: 0.36159753799438)

    $result = [];
    foreach ($array as $k => $v) {
        $result[$prefix . $k] = $v;
    }
    
  2. array_combine(),array_map(),array_keys(): (PHP8.2.5 exec time: 0.52124261856079)

    $result = array_combine(
        array_map(
            fn($k) => $prefix . $k,
            array_keys($array)
        ),
        $array
    );
    
  3. array_walk(): (PHP8.2.5 exec time: 0.77606439590454)

    $result = [];
    array_walk(
        $array,
        function($v, $k, $prefix) use (&$result) {
            $result[$prefix . $k] = $v;
        },
        $prefix
    );
    
  4. array_reduce(),array_keys(): (PHP8.2.5 exec time: 49.715828895569)

    $result = array_reduce(
        array_keys($array),
        function($res, $k) use ($array, $prefix) {
            $res[$prefix . $k] = $array[$k];
            return $res;
        },
        []
    );
    

    Using arrow function syntax with array_reduce() does not appear to improve performance.

    $result = array_reduce(
        array_keys($array),
        fn($result, $k) => $result + [$prefix . $k => $array[$k]],
        []
    );
    
  5. generator: (PHP8.2.5 exec time: 0.58324337005615)

    function generator($array, $prefix) {
        foreach ($array as $k => $v) {
            yield $prefix . $k => $v;
        }
    }
    $result = iterator_to_array(generator($array, $prefix));
    

Upvotes: 0

Sabrina
Sabrina

Reputation: 2799

Another way to write @Lode's first snippet using arrow function syntax which is available from php7.4+:

function prefixArrayKeys($array, $prefix) {
    return array_combine(
        array_map(fn($k) => $prefix . $k, array_keys($array)),
        $array
    );
}

Tests:

function testPrefixArrayKeys() {
    // Test case 1
    $array = ['name' => 'John', 'age' => 30];
    assert(prefixArrayKeys($array, 'prefix_') == ['prefix_name' => 'John', 'prefix_age' => 30]);
    
    // Test case 2
    $array = ['city' => 'New York'];
    assert(prefixArrayKeys($array, '') == ['city' => 'New York']);
    
    // Test case 3
    $array = [];
    assert(prefixArrayKeys($array, 'prefix_') == []);
    
    // Test case 4
    $array = [1 => 'One', 2 => 'Two'];
    assert(prefixArrayKeys($array, 'prefix_') == ['prefix_1' => 'One', 'prefix_2' => 'Two']);
    
    echo 'All test cases passed!';
}

Upvotes: 4

Lode
Lode

Reputation: 1105

Could do this in one long line I presume:

$prefix = "prefix";
$array = array_combine(
    array_map(
        function($k) use ($prefix) {return $prefix . $k; }, 
        array_keys($array)
     ),
    $array
);

Or -- as Sabrina points out in their answer -- using an arrow function syntax which is available from php7.4+.

There's probably dozens of ways to do this though:

$prefix = "prefix";
foreach ($array as $k => $v)
{
    $array[$prefix . $k] = $v;
    unset($array[$k]);
}

Now historically for versions of PHP prior to 5.3:


$prefix = "prefix";
$array = KeyPrefixer::prefix($array, $prefix);

class KeyPrefixer
{
    private $prefix;
    public function __construct($prefix) {
        $this->prefix = (string)$prefix;       
    }

    public static function prefix(array $array, $prefix)
    {
        $prefixer = new KeyPrefixer($prefix);
        return $prefixer->mapArray($array);
    }

    public function mapArray(array $array)
    {
        return array_combine(
            array_map(array($this, 'mapKey', array_keys($array)),
            $array
        );
    }

    public function mapKey($key) {
        return $this->prefix . (string)$key;
    }
}

which was originally without the $prefix as parameter and with a call to create_function(), which should not be used due to high in-own-foot-shooting-potential. Only for reference:

$array = array_combine(
    array_map(create_function('$k', 'return "prefix$k";'), array_keys($array)),
    $array
);

Direct compare with arrow function:

$array = array_combine(
    array_map(fn($k) => "prefix$k", array_keys($array)),
    $array
);

Upvotes: 76

user11487450
user11487450

Reputation:

Here's a fast, one-liner solution (supported on PHP 4+) to add a prefix and/or suffix using implode / explode:

$array = range(0, 1000000);
$delimiter = '-';
$prefix = 'string';
$suffix = '';

$result = array_combine(explode($delimiter, $prefix . implode($suffix . $delimiter . $prefix, array_keys($array)) . $suffix), $array);

Upvotes: 0

mistajolly
mistajolly

Reputation: 481

function keyprefix($keyprefix, Array $array) {

    foreach($array as $k=>$v){
        $array[$keyprefix.$k] = $v;
        unset($array[$k]);
    }

    return $array; 
}

Using array_flip will not preserve empty or null values. Additional code could be added in the unlikely event that the prefixed key already exists.

Upvotes: 7

TarranJones
TarranJones

Reputation: 4242

I would create a completely new array, and create your new keys. That has to be faster than unsetting all unwanted keys;

$prefixed_array = array();

foreach ($array as $key => $value) {
    $prefixed_array[ $prefix . $key] = $value;
}

And if you want to do any other "affix"'s

function array_affix_keys($affix, Array $array, $type = 'prefix', $options = array()){

    $affixed_array = array();

    if($type =='prefix'){
        foreach ($array as $key => $value) {$affixed_array[ $affix . $key] = $value;}
        return $affixed_array;
    }
    if($type =='suffix'){
        foreach ($array as $key => $value) {$affixed_array[$key . $affix ] = $value;}
        return $affixed_array;
    }
    if($type =='circumfix'){

        if(is_array($affix) && count($affix) == 2){

            foreach ($array as $key => $value) {
                $affixed_array[ $affix[0] . $key . $affix[1] ] = $value;
            }
        }
        return $affixed_array;
    }
    if($type == 'simulfix' && isset($options['phonemes'])){
        foreach ($array as $key => $value) { $affixed_array[ str_replace($options['phonemes'], $affix, $key) ] = $value;}
        return $affixed_array;
    }
    return $array;
}


$prefixed = array_affix_keys('prefix_', $array);
$prefixed = array_affix_keys('prefix_', $array, 'prefix');

$suffixed = array_affix_keys('_suffix', $array, 'suffix');
$circumfixed = array_affix_keys(array('prefix', 'suffix'), $array, 'circumfix');
$simulfix = array_affix_keys('replace', $array, 'simulfix', array('phonemes' => 'find'));

Upvotes: 0

Alpesh Patel
Alpesh Patel

Reputation: 41

function array_key_prefix_suffix(&$array,$prefix='',$suffix=''){
        $key_array = array_keys($array);
        $key_string = $prefix.implode($suffix.','.$prefix,$key_array).$suffix;
        $key_array = explode(',', $key_string);
        $array = array_combine($key_array, $array);
    }

This is implemented and working very well

Upvotes: 2

adamS
adamS

Reputation: 600

Another way to do achieve is with array_flip()

<?php 
    $data = array_flip($data);
    foreach($data as $key => &$val) { $val = "prefix" . $val; }
    $data = array_flip($data);

Upvotes: -1

fracz
fracz

Reputation: 21248

I figured out one-line solution:

array_walk($array, create_function('$value, &$key', '$key = "prefix" . $key;'));

Upvotes: -1

codaddict
codaddict

Reputation: 455122

If you don't want to use for loop you can do:

// function called by array_walk to change the $value  in $key=>$value.
function myfunction(&$value,$key) {
    $value="prefix$value";
}

$keys = array_keys($array);  // extract just the keys.
array_walk($keys,"myfunction"); // modify each key by adding a prefix.
$a = array_combine($keys,array_values($array)); // combine new keys with old values.

I don't think this will be more efficient than the for loop. I guess array_walk will internally use a loop and there is also the function call overhead here.

Upvotes: 2

Related Questions