24

Looping is time consuming, we all know that. That's exactly something I'm trying to avoid, even though it's on a small scale. Every bit helps. Well, if it's unset of course :)

On to the issue

I've got an array:

array(3) {
    '0' => array(2) {
        'id'   => 1234,
        'name' => 'blablabla',
    },
    '1' => array(2) {
        'id'   => 1235,
        'name' => 'ababkjkj',
    },
    '2' => array(2) {
        'id'   => 1236,
        'name' => 'xyzxyzxyz',
    },
}

What I'm trying to do is to convert this array as follows:

array(3) {
    '1234' => 'blablabla',
    '1235' => 'asdkjrker',
    '1236' => 'xyzxyzxyz',
}

I guess this aint a hard thing to do but my mind is busted right now and I can't think of anything except for looping to get this done.

Peter
  • 8,776
  • 6
  • 62
  • 95
  • A foreach isn't exactly a loop - it will iterate through the array ONCE. If you get your output array to be what it needs to be on each pass (which seems trivial given your requirements) it doesn't seem a "waste of resources" at all. – Fluffeh Jul 03 '15 at 09:11
  • 12
    Either you write the loop, or you call a sexy php function that will do the looping for you. – RiggsFolly Jul 03 '15 at 09:11
  • 1
    I don't think you can't avoid a cycle: logically , you must check each element => you need to go from the start to the end of the array. in C/C++ you probably could move the pointer by a specific offset, but i don't think you can apply something similar here – Stormsson Jul 03 '15 at 09:15
  • 5
    I have to say more: don't try to avoid looping! Plain for looping is the fastest solution. Look at my [benchmarks](http://stackoverflow.com/questions/31202835/how-to-set-an-array-value-as-array-key-without-looping/31203043#31203043). – Ivan Velichko Jul 03 '15 at 10:03
  • So you want us to do benchmarking? – Salman A Jul 03 '15 at 11:05
  • I've already done benchmarks for almost all offered solutions in my [answer](http://stackoverflow.com/questions/31202835/how-to-set-an-array-value-as-array-key-without-looping/31203043#31203043). – Ivan Velichko Jul 03 '15 at 11:14
  • @Ostrovski: sorry the comment was for OP. The question looks too broad to me. – Salman A Jul 03 '15 at 11:49
  • Looping is time consuming *while coding*. – johnchen902 Jul 03 '15 at 14:52
  • @Peter you had an open bounty what is your expected array – Narendrasingh Sisodia Dec 17 '15 at 13:37
  • Loop unrolling will help you to reduce the number of comparison if you can make it work (https://en.wikipedia.org/wiki/Loop_unrolling). – Kazaag Dec 23 '15 at 16:15

6 Answers6

29

Simply use array_combine along with the array_column as

array_combine(array_column($array,'id'), array_column($array,'name'));

Or you can simply use array_walk if you have PHP < 5.5 as

$result = array();
array_walk($array, function($v)use(&$result) {
    $result[$v['id']] = $v['name']; 
});

Edited:

For future user who has PHP > 5.5 can simply use array_column as

array_column($array,'name','id');

Fiddle(array_walk)

Narendrasingh Sisodia
  • 21,247
  • 6
  • 47
  • 54
  • 2
    Though this is quite an old question, I just stumbled over it. Simply `array_column($input, 'name', 'id')` will produce the same output (reference: [`index_key`](https://php.net/manual/function.array-column.php)). – Yoshi Sep 10 '15 at 09:36
  • 2
    `array_walk` is still a loop at heart it's an iterator the same as a `for` (you define the iteration system based on integer) or `foreach` (it's a basic key to value iteration system one at a time iteration), however the other methods use iterators it's just how much of it do you want Compiled code compared to scripted code :) – Barkermn01 Dec 22 '15 at 12:53
8

UPD: Warning the slowest solution! See benchmarks below.

Try this code:

$a = array(array('id'   => 1234,
                 'name' => 'blablabla'),
           array('id'   => 1235,
                 'name' => 'ababkjkj'),
           array('id'   => 1236, 
                 'name' => 'xyzxyzxyz'));

var_export(array_reduce($a, function($res, $item) {
    $res[$item['id']] = $item['name'];
    return $res;
}));

Works fine even in PHP 5.3. And uses only one function array_reduce.

UPD: Here are some benchmarks (PHP 5.6 over Debian 7 on a medium quality server):

$a = [];
for ($i = 0; $i < 150000; $i++) {
    $a[$i] = ['id' => $i,
               'name' => str_shuffle('abcde') . str_shuffle('01234')];
}

$start = microtime(true);

if (false) {
    // 7.7489550113678 secs for 15 000 itmes
    $r = array_reduce($a, function($res, $item) {
             $res[$item['id']] = $item['name'];
             return $res;
         });
}

if (false) {
    // 0.096649885177612 secs for 150 000 items
    $r = array_combine(array_column($a, 'id'),
                       array_column($a, 'name'));
}

if (true) {
    // 0.066264867782593 secs for 150 000 items
    $r = [];
    foreach ($a as $subarray) {
        $r[$subarray['id']] = $subarray['name'];
    }
}

if (false) {
    // 0.32427287101746 secs for 150 000 items
    $r = [];
    array_walk($a, function($v) use (&$r) {
        $r[$v['id']] = $v['name'];
    });
}

echo (microtime(true) - $start)  . ' secs' . PHP_EOL;

So, as a conclusion: plain iteration with simple for loop is a winner (as mentioned in this answer). On a second place there is array_combine allowed only for new versions of PHP. And the worst case is using my own solution with closure and array_reduce.

Community
  • 1
  • 1
Ivan Velichko
  • 6,348
  • 6
  • 44
  • 90
  • Why do you use array_reduce instead of array_map ? – OIS Jul 03 '15 at 09:32
  • By semantic reasons. You have an array as the input and you want to obtain single element on the output. Despite of it is array of arrays on the input and just and array on the output. – Ivan Velichko Jul 03 '15 at 09:36
  • @OIS `array_map` can transform the values, but not the keys. If we tried, we'd get something like `[0 => 'blablabla', 1 => 'asdkjrker', 2 => 'xyzxyzxyz']`. We *could* build up the result as a side-effect, eg. `array_map(function($val) use (&result) { $result[$val['id']] = $val['name']; }, $input)` but iteratively building up values is exactly what `array_reduce` is for :) – Warbo Jul 03 '15 at 12:06
7

If you have php >= 5.5:

$res = array_combine(array_column($source, 'id'), array_column($source, 'name'));

If not - make a loop.

u_mulder
  • 54,101
  • 5
  • 48
  • 64
  • 3
    Its sexy, but something somewhere is doing the looping – RiggsFolly Jul 03 '15 at 09:10
  • Huh, how can you avoid looping even on the background? I think question is about avoiding looping in the user space. – Ivan Velichko Jul 03 '15 at 09:11
  • 2
    Potentially this will be 3 loops instead of one! if you actually think about it. Admittedly it will be a piece of `C` code doing the looping but do 3x`c` loops run faster than 1x`php` loops – RiggsFolly Jul 03 '15 at 09:14
  • @RiggsFolly no, array function loops are slower than php language construct foreach. 3 array functions even more so. – OIS Jul 03 '15 at 09:50
  • @OIS I ment that using `array_combine()` with 2 `array_column()` functions would cause 3 loops even though they are `C` based. Doing it yourself in PHP would only be one. – RiggsFolly Jul 03 '15 at 10:22
  • But PHP loops very slow, using build in functions to let a more efficient language take care of it, is 9 out of 10 times a lot faster – Martijn Jul 03 '15 at 11:00
4

Make use of array_map function, (PHP 4 >= 4.0.6, PHP 5)

[akshay@localhost tmp]$ cat test.php
<?php

$array = array(
                array('id'   => 1234,'name' => 'blablabla'),
                array('id'   => 1235,'name' => 'ababkjkj'),
                array('id'   => 1236,'name' => 'xyzxyzxyz')
              );

$output = array();
array_map(function($_) use (&$output){ $output[$_['id']] = $_['name']; },$array);

// Input
print_r($array);

// Output
print_r($output);

?>

Output

[akshay@localhost tmp]$ php test.php
Array
(
    [0] => Array
        (
            [id] => 1234
            [name] => blablabla
        )

    [1] => Array
        (
            [id] => 1235
            [name] => ababkjkj
        )

    [2] => Array
        (
            [id] => 1236
            [name] => xyzxyzxyz
        )

)
Array
(
    [1234] => blablabla
    [1235] => ababkjkj
    [1236] => xyzxyzxyz
)
Akshay Hegde
  • 16,536
  • 2
  • 22
  • 36
  • why down vote ? whats wrong ? please let me know the reason – Akshay Hegde Jul 03 '15 at 12:10
  • This solution is abusing the purpose of `array_map`, which could cause confusion and hence bugs. `array_map` is meant to create `[f(a), f(b), f(c)]` from `f` and `[a, b, c]`, in a way which avoids cross-iteration interference and having to manage the order of evaluation. Here we're ignoring the result, we have cross-iteration interference (updating the same `$output`), and we must take the order of evaluation into account (eg. for duplicate IDs). The purpose of `array_reduce` matches this problem more closely: building a result iteratively in a specified order. – Warbo Jul 03 '15 at 12:20
  • 1
    I disagree with what you said but anyways thanks for the time you spent for comment. – Akshay Hegde Jul 03 '15 at 14:32
3

This is the fastest and simplest code here so far ...

$result = [];
foreach ($input as $subarray) {
    $result[$subarray["id"]] = $subarray["name"];
}
OIS
  • 9,833
  • 3
  • 32
  • 41
0

Try this function:

array_combine(array_column($array,'id'), array_column($array,'name'));
Waqas Shahid
  • 1,051
  • 1
  • 7
  • 22