4

Pretty straightforward:

// turn
array('foo', 'bar', 'hello', 'world');

// into
array('foo' => 'bar', 'hello' => 'world');

Right now I'm using:

do{
    $arrayOut[current($arrayIn)] = next($arrayIn);
}while(next($arrayIn));

I'm wondering if there's a way to do it without the intermediary variable, $arrayOut. I could write a function, however this is a single use case, and I'm trying to keep my script uncluttered. I'm just wondering if there's something I missed in the docs that would serve this purpose.


The values are coming from a routing path:

route/to/controller/action/key1/value1/key2/value2

It's exploded, and eventually after using the other components, I'm left with ('key1', 'value1', 'key2', 'value2', ...)


Thank you folks for the insight and suggestions. Long Ears won this one for the concise approach, which when expanded to more than "1 line", is not terribly cryptic (I don't think at least)

However, also with regard to Long Ears' suggestion, perhaps my desire for semantically precise code with minimal verbosity has gotten the better of me, and I was chasing daisies trying to keep my variable scope "pollutant-free", to paraphrase myself.

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
  • Related: [Convert every two values of an associative array into key-value pairs](https://stackoverflow.com/a/71952914/2943403) – mickmackusa Aug 31 '22 at 08:12

4 Answers4

5

You can make your existing code slightly more efficient (removes one function call per iteration, for one at the beginning:)

$b = array();
array_unshift($a, false);
while (false !== $key = next($a)) {
    $b[$key] = next($a);
}

Ok, (shudder) here's your one liner:

$b = call_user_func_array('array_merge', array_map(function($v) { return array($v[0] => $v[1]); }, array_chunk($a, 2)));
Long Ears
  • 4,886
  • 1
  • 21
  • 16
  • **Thanks Long Ears;** Nice one, though efficiency is not so much an issue as scope pollution. I'll certainly keep that in mind for similar operations on larger data. – Dan Lugg Mar 09 '11 at 01:25
  • @Tomcat If you're worried about scope pollution then just put it in a function, although if it's just one temporary variable then really simplicity should win. – Long Ears Mar 09 '11 at 01:28
  • **@Long Ears;** Perhaps... It really irks me to see garbage in intellisense. Perhaps I'm just finicky. – Dan Lugg Mar 09 '11 at 01:34
  • 2
    @Tomcat I'd say intellisense is too finicky if that throws some kind of warning. As a coder your first responsibility is to make your code easy to understand. Occasionally that conflicts with your second responsibility - to make your code efficient. However neither of those should *ever* be compromised for (arguable) aesthetics. – Long Ears Mar 09 '11 at 01:39
  • **@Long Ears;** Very good point. As both a web/graphic designer and developer, I sometimes get my priorities tangled. Aesthetics should be left for UIs. – Dan Lugg Mar 09 '11 at 01:41
  • @Tomcat updated to include what I think you're looking for, although I stand by the rule of simplicity. – Long Ears Mar 09 '11 at 02:08
  • @Long Ears: +1 because I 've never ever used `array_chunk` before :) Nice version! – Jon Mar 09 '11 at 10:27
3

You do not need an array, you can do it directly with the path text:

$path = "foo/bar/hello/world";

preg_match_all("#(?J)(?<key>[^/]+)/(?<val>[^/]*)#", $path, $p);
$params = array_combine($p['key'], $p['val']);

print_r($params);
/*
Array
(
    [foo] => bar
    [hello] => world
)
*/
2

I don't think you can do better than what you have. But what about trying to put the odd and even elements in different arrays when your input is read? You could then use array_combine to make an one-liner.

Update: array_map won't help, as it will produce an array with an equal number of elements. You can do things with array_filter and array_combine, but again, that won't end up being shorter.

I really like what you have; short, simple, does the job. I 'd simply refactor it out into a function, let's say array_interleave_combine or some such.

Update 2:

Well, you asked for it, so here it is. Tries to be too clever if you ask me. Is an one-liner, but I really really don't think the "pureness" of this is enough to pay back the lost time someone would need to understand what's going on:

$result = array_reduce(
    array('foo', 'bar', 'hello', 'world'),
    function($partial, $item) {
        static $nextKey;
        if ($nextKey === null) {
            $nextKey = $item;
        }
        else {
            $partial[$nextKey] = $item;
            $nextKey = null;
        }

        return $partial;
    });
Jon
  • 428,835
  • 81
  • 738
  • 806
  • **Thanks Jon;** Hmm, `array_combine` could work. I'm thinking `array_map` with a lambda to keep my method scope clean; See edits for elaboration. – Dan Lugg Mar 09 '11 at 01:21
  • @TomcatExodus: I thought about that before answering. But I believe it simply won't be shorter than what you already have. – Jon Mar 09 '11 at 01:24
  • **@Jon;** It's not so much "short", than non-pollutant to scope I'm looking for, which is why a lambda would be fine. I'll play around with it. – Dan Lugg Mar 09 '11 at 01:28
  • I think you meant a 13-liner ;) – Long Ears Mar 09 '11 at 01:50
  • @Long Ears: Please excuse me, I 'm just too used to addressing the compiler. ;) – Jon Mar 09 '11 at 01:55
1

This solution does not need an additional array:

$arr = array('foo', 'bar', 'hello', 'world');

for($i = 0, $count = count($arr); $i < $count; $i += 2)
{
  $arr[$arr[$i]] = $arr[$i + 1];
  unset($arr[$i], $arr[$i + 1]);
}
  • 1
    @TomcatExodus shouldn't be an issue as long as your array has an even number of elements. To be certain make the condition `$i + 1 < $count` – Jacob Mar 09 '11 at 01:35
  • 1
    I 'm kind of nitpicking, but try this with `$arr = array('1', 'bar', 'hello', 'world');`; you get the picture. If an array element is the string representation of an integer you have not already seen, you are going to trash it. – Jon Mar 09 '11 at 01:42
  • @Jon: Yea, that is a disadvantage of it. I think using a temporary array is the best option. –  Mar 09 '11 at 01:49