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.

10
  • 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. Commented Jul 3, 2015 at 9:11
  • 12
    Either you write the loop, or you call a sexy php function that will do the looping for you. Commented Jul 3, 2015 at 9: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 Commented Jul 3, 2015 at 9:15
  • 5
    I have to say more: don't try to avoid looping! Plain for looping is the fastest solution. Look at my benchmarks. Commented Jul 3, 2015 at 10:03
  • So you want us to do benchmarking? Commented Jul 3, 2015 at 11:05

6 Answers 6

29
+50

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)

Sign up to request clarification or add additional context in comments.

2 Comments

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

3 Comments

Why do you use array_reduce instead of array_map ?
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.
@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 :)
7

If you have php >= 5.5:

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

If not - make a loop.

6 Comments

Its sexy, but something somewhere is doing the looping
Huh, how can you avoid looping even on the background? I think question is about avoiding looping in the user space.
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 3xc loops run faster than 1xphp loops
@RiggsFolly no, array function loops are slower than php language construct foreach. 3 array functions even more so.
@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.
|
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
)

3 Comments

why down vote ? whats wrong ? please let me know the reason
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.
I disagree with what you said but anyways thanks for the time you spent for comment.
3

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

$result = [];
foreach ($input as $subarray) {
    $result[$subarray["id"]] = $subarray["name"];
}

Comments

0

Try this function:

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.