3
Array
(
    [0] => Array
        (
            [color] => Brown
        )

    [1] => Array
        (
            [color] => Green
        )

    [2] => Array
        (
            [width] => 34
        )

)

i need to make it like this

[color] => Array
    (
        [0] => green
        [1] => brown
    )

[width] => Array
    (
        [0] => 34
    )

)

i am trying with all the array tools. But i cant make it like i want it.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136

6 Answers6

7

This is fairly simple with array_column() (requires PHP >= 5.5.0):

$result = array[
  'color' => array_column($arr, 'color'),
  'width' => array_column($arr, 'width')
];

Live fiddle: https://eval.in/81746


If you do not know the keys beforehand, here is another solution using array_walk_recursive():
$result = [];
array_walk_recursive($arr, function($value, $key) use (&$result) {
  if (!isset($result[$key])) {
    $result[$key] = [];
  }
  $result[$key][] = $value;
});

Live fiddle: https://eval.in/81745

ComFreek
  • 29,044
  • 18
  • 104
  • 156
  • This _does_ assume the OP knows what keys he's dealing with, and that they'll all be set at any given time, what if that's an unknown, or what if you'd have to maintain code like this, and find yourself gradually adding `array_column` calls + keys as your project grows? – Elias Van Ootegem Dec 21 '13 at 16:56
  • @EliasVanOotegem My original solution was never intended to be used with a high or variable amount of keys. See my update, please. – ComFreek Dec 21 '13 at 17:01
  • That ought to work, but I can't see why you should make it so complicated: `array_walk_recursive` + instance of `Closure` + the dreaded `&$result` (that one can bite you in the back) is so much more verbose and causes so much more overhead than my initial, simple approach of loop + built-in array function – Elias Van Ootegem Dec 21 '13 at 17:09
  • @EliasVanOotegem Well, my solution is more elegant in my opinion. However, your answer doesn't even produce the same output (`width` is not an array). My code is also slightly faster ([benchmark](https://eval.in/81757)). I know, this micro-optimization is probably not worth it, but I still find it interesting that closures aren't as expensive as I thought. – ComFreek Dec 21 '13 at 17:16
  • 1
    Your approach being more elegant is subjective. I happen to strongly dislike closures and lambda's _in PHP_, love them in scheme or JS, but not in PHP. Your benchmark did surprize me at first, but it's not a valid test, really: PHP doesn't have block scope, so it caches your instance of Closure, and you declare the assoc array when timing my approach. I've poured both our takes into functions, predeclare all used vars (except for the `$time` vars), and then, [My approach was faster](https://eval.in/81769). – Elias Van Ootegem Dec 21 '13 at 17:34
  • By putting the code in functions (with their own scope) all created and used variables are GC'ed each time the code gets executed - better resembling actual performance of a snippet of code. The difference is, still, ever so minor, though. but I couldn't help being pedantic and explain why I bothered setting up a separate benchmarking script – Elias Van Ootegem Dec 21 '13 at 17:37
  • @EliasVanOotegem I appreciate your corrections! Thank your for taking time to write those comments. +1 from me. – ComFreek Dec 21 '13 at 17:42
  • 1
    Don't mention it... I like benchmarking stuff, and I'm still surprized how little overhead a `Closure` instance generates in PHP 5.5. I might have to reconsider my preconceived notions on the matter, especially when using Symphony2 form-events. Glad to see you're not mistaken my enthousiasm and slight obsessiveness for being competitive or as me being a twat, that _has_ to be right all the time. :-) – Elias Van Ootegem Dec 21 '13 at 17:52
4

So you want to merge the arrays recursively... if only such an array_merge_recursive function existed... Why don't you try this:

$a = array(
    array('colour' => 'green'),
    array('colour' => 'blue'),
    array('width' => 123)
);
$result = array();
foreach($a as $arr)
{
    $result = array_merge_recursive($result, $arr);
}
var_dump($result);

That worked pretty darn well for me, as you can see for yourself here, too

True, in the given example width won't be an array, so you get:

array('colour' => array('green','blue'),'width' => 123);

If you need everything to be an array, then a dirty fix would be to use a cast:

foreach($result as $k => $v) $result[$k] = (array) $v;

Reassigning the $result values a second time, only casting them as an array ensures that all values will, evidently, be arrays. An array that is cast to an array will remain unchanged, just like (int) 1 still evaluates to 1. A primitive value (strings, integers, doubles,...) will be wrapped int an array, but an object will be transformed into an array, so be careful. If objects are likely to occur in this array:

foreach($result as $k => $v) $result[$k] = is_array($v) ? $v : array($v);

is probably the safer bet. However, I chose not to go for this approach as I still find it pretty tedious and silly to wrap everything up into an array, containing only 1 value...

For those of you with a strange preference for unmaintainable code, the following one-liner is a condensed, but notice free & working example of the same code:

foreach($a as $arr) $result = array_merge_recursive(isset($result) ? $result : array(), $arr);

This is as a response to Stuart Wakefield who suggested a one-liner using call_user_func_array, which is something I'll always oppose, for as long as I live and breathe, BTW...

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • You could simply do this as a one liner `$result = call_user_func_array('array_merge_recursive', $arr);` – Stuart Wakefield Dec 21 '13 at 17:25
  • @StuartWakefield: that would reduce maintainability and add overhead in the form of a second function call. A loop is faster, and easier to read... besides `foreach($arr as $a) $result = array_merge_recursive(isset($result) ? $result : array(), $a);` is a one-liner, too :) – Elias Van Ootegem Dec 21 '13 at 17:39
  • I totally disagree that it reduces maintainability, less code = less to maintain and actually there is a reduction of function calls. 2 calls, one to call_user_func_array and one to array_merge_recursive, whereas the loop solution results in count($a) x function calls... – Stuart Wakefield Dec 21 '13 at 17:43
  • guys how can i get instead of [0] green [1] brown [0] array(green, brown) without loosing width – Anthony De Meulemeester Dec 21 '13 at 17:43
  • 2
    @StuartWakefield "Less code, less to maintain" does not always hold true. Besides that, I would really add a comment in the code explaining your one-liner. – ComFreek Dec 21 '13 at 17:45
  • @AnthonyDeMeulemeester: you'll have to elaborate on that, m8... my answer gives you `array( colour => array(green, blue), width => 123);` – Elias Van Ootegem Dec 21 '13 at 17:45
  • @StuartWakefield: If less code = less maintenance, we'd all be writing Lisp. Why aren't we: it's bloody hard to maintain! easy to read code == easy to maintain code (to a degree). besides: yes, my loop results in count($array) function calls, but do you honestly thing `array_walk` or `array_map` doesn't iterate over the array? come on, you need to take a look at the PHP source for the array functions, neigh on all of them iterate over the zvals – Elias Van Ootegem Dec 21 '13 at 17:48
  • @EliasVanOotegem I'm not for a second saying that your single liner is maintainable, nor am I saying that single liners are indeed maintainable, however, if you learn the PHP API, use the tools it provides rather than sticking to the basics then you can benefit from all of this functionality. My approach, one function call... of course comment and link to the API docs. BTW it is also 25% faster than the loop approach. – Stuart Wakefield Dec 21 '13 at 17:58
  • Talking about the functional equivalent of your the loop: `$result = call_user_func_array('array_merge_recursive', $arr);` and not the approach in my answer, which normalizes the output – Stuart Wakefield Dec 21 '13 at 18:00
  • @StuartWakefield: Fork the benchmark links in the comment section below ComFreek's answer, and show the 25% speed difference. Following the PHP API isn't always the best idea, IMO. PHP is a language designed by afterthought, not as a consistent and modulare API... heck, PHP wasn't even designed, it was never intended to be a language. Replacing loops for function calls is like replacing `$anArray[] = $newElem;` with `array_push($anArray, $newElem);`, even the docs say it adds overhead: language construct vs function: language construct wins. Keep it Simple. – Elias Van Ootegem Dec 21 '13 at 18:04
  • @EliasVanOotegem also adding in the normalization fix, via functional calls vs for loops, the functional variation still wins, although only by 7.5% https://eval.in/81787 – Stuart Wakefield Dec 21 '13 at 18:25
  • 1
    @StuartWakefield: Interesting... switching down to 5.4 significantly reduces the performance of `call_user_func_array`, though... I guess there have been a lot of engine improvements concerning Closure's and the like +, of course `call_user_func_array` adds function call, but reduces number of assignments, I guess that's where the performance benefit is coming from. Still, not my preferred coding style, but you're right, it turns out to be faster. – Elias Van Ootegem Dec 22 '13 at 12:30
1

I guess this should do it, especially if you don't know what keys you will have:

foreach ($original_array as $val1)
    foreach ($val1 as $key2=>$val2)
        $merged_array[$key2][] = $val2;
nl-x
  • 11,762
  • 7
  • 33
  • 61
  • Doesn't `$merged_array[$key2]` need to be initialized to an array? – Eric Dec 21 '13 at 16:35
  • @Eric That's the way I had it first indeed. But later I realized it isn't really needed. (Look at my edit history.) – nl-x Dec 21 '13 at 16:39
  • Is that a documented feature of the `[]` syntax? – Eric Dec 21 '13 at 16:54
  • @Eric: somewhere, it'll be mentioned in the documents, but essentially it's syntactic sugar for `array_push($arr, $val)` – Elias Van Ootegem Dec 21 '13 at 16:57
  • @Eric: beats me. I threw it in phpFiddle and it worked without any notice, warning or error. – nl-x Dec 21 '13 at 16:58
  • @EliasVanOotegem: I knew that bit, but `array_push` doesn't work if `$arr` is null – Eric Dec 21 '13 at 16:58
  • [Found it](http://www.php.net/array_push): _"**Note:** `array_push()` will raise a warning if the first argument is not an array. This differs from the `$var[]` behaviour where a new array is created."_ – Eric Dec 21 '13 at 17:00
  • @Eric: yes but `$undefinedVar[] = 123;` _does_ raise a notice, try it with display_errors + `E_STRICT | E_ALL` – Elias Van Ootegem Dec 21 '13 at 17:01
  • @EliasVanOotegem I even tried that already. It didn't show notices in phpFiddle at least, even with `error_reporting(E_ALL | E_STRICT); ` – nl-x Dec 21 '13 at 17:04
  • @nl-x: try a cli script, and set the error reporting levels + display errors in the ini file... trust me, you'll see the notices – Elias Van Ootegem Dec 21 '13 at 17:11
  • @EliasVanOotegem So would it be a php.ini setting, about how strict to be with undefined vars ? Or a php version thing ? Because CLI and non-CLI should show the same behavior. And did you see Eric's 'found it' in the comment above? – nl-x Dec 21 '13 at 17:15
  • @nl-x: ini-setting: of course, when developing, always use an ini file that is as strict as it gets. found-it: yes, I saw that, but I said: _"raise a notice"_, it will initialize the var to a new array, but a notice will be generated, too – Elias Van Ootegem Dec 21 '13 at 17:16
  • @EliasVanOotegem So which directive do you think is suppressing the notice? – nl-x Dec 21 '13 at 17:35
  • @nl-x: On PHPFiddle? Don't know (don't care to be honest), but I'd say they don't display errors/notices for security reasons and sandbox your code, meaning notices are _very_ unlikely to show up... again: run the same code from a cli-script and check for notices there – Elias Van Ootegem Dec 21 '13 at 18:07
  • @EliasVanOotegem : but they do show notices, warnings and errors. Just not this one of `$undefinedFoo[] = 'bar';` that you say should give a notice in CLI. That's why I'm so curious. – nl-x Dec 21 '13 at 18:14
  • @nl-x: They show errors, not notices, when you try to set `ini_set('display_errors',1);` it returns _"You input disabled function(s) by PhpFiddle : ini_set('display_errors', 1)"_... – Elias Van Ootegem Dec 21 '13 at 18:21
  • @nl-x: Those are different notices all together: unexpected string, division by zero and undefined function are bigger problems than an uninitialized variable, or using variable that was initialized to null as an array. Just _run a cli script_ if you don't believe me... pointing at phpfiddle and saying they do show notices (in some cases) isn't relevant. Try with a script in an environment _you_ can control and set up. – Elias Van Ootegem Dec 22 '13 at 12:36
0

just use foreach like below -- arrayName = your original array --

foreach($arrayName as $newArr){
 if($newArr['color']){
   $tempArr['color'][] = $newArr['color'];
  }
 if($newArr['width']){
  $tempArr['width'][] = $newArr['width'];
 }
}
Abhishek
  • 1,543
  • 3
  • 13
  • 29
0

Building upon Elias's array_merge_recursive answer, the following introduces a small fix to turn single item merge into an array:

/* This version uses the function array_merge_recursive to collect
 * all of the values for the nested arrays by key
 *
 * @see http://willem.stuursma.name/2011/09/08/parallel-array_map-with-hiphop/
 * @see http://willem.stuursma.name/2010/11/22/a-detailed-look-into-array_map-and-foreach/ 
 * for why for loops are better than array_map in general
 */
$result = array_map(function($item) {

    /* The array_merge_recursive function doesn't add
     * values to an array if there was only one found
     * if the item isn't an array, make it an array
     */
    return is_array($item) ? $item : array($item);

/* Calls the array_merge_recursive function applying all of
 * the nested arrays as parameters.
 *
 * @see http://php.net/array_merge_recursive
 * @see http://www.php.net/call_user_func_array
 */
}, call_user_func_array('array_merge_recursive', $arr));

Produces:

Array 
    (
        [color] => Array
            (
                [0] => green
                [1] => brown
            )

        [width] => Array
            (
                [0] => 34
            )
    )

Instead of:

Array 
    (
        [color] => Array
            (
                [0] => green
                [1] => brown
            )

        [width] => 34
    )

Alternatively, a dynamic approach to ComFreek's array_column solution.

This gives you the array of the keys:

/* Gets the keys of the nested arrays as a single array of keys by first
 * mapping the nested arrays to an array of keys they contain and then
 * by merging these arrays and killing duplicates
 *
 * @see http://php.net/function.array-unique
 * @see http://www.php.net/call_user_func_array
 * @see http://www.php.net/array_merge
 * @see http://www.php.net/array_map
 */
$keys = array_unique(call_user_func_array('array_merge', array_map(function($item) {

    /* Replaces the nested array of keys and values with an array
     * of keys only in the mapped array
     *
     * @see http://www.php.net/array_keys
     */
    return array_keys($item);
}, $arr)));

As:

Array
    (
        [0] => color
        [1] => width
    )

Which can be used with this snippet:

/* Combines the array of keys with the values from the nested
 * arrays.
 *
 * @see http://php.net/array_combine
 * @see http://www.php.net/manual/en/function.array-map.php
 */
$result = array_combine($keys, array_map(function($key) use($arr) {

    /* Collects the values from the nested arrays
     *
     * @see http://php.net/array_column
     */
    return array_column($arr, $key);
}, $keys));

To create the desired output:

Array 
    (
        [color] => Array
            (
                [0] => green
                [1] => brown
            )

        [width] => Array
            (
                [0] => 34
            )
    )

Note: functional calls can be beneficial in most languages over an imperative style although it does require a shift mentally. Functional patterns open up the possibilities of low level optimizations that otherwise wouldn't be possible. Array map, for example, could be executed in parallel, whereas a for loop cannot, the for loop will always have a restriction that it must execute sequentially.

Stuart Wakefield
  • 6,294
  • 24
  • 35
  • Total overkill: an instance of `closure` + `call_user_func_array` is completely over the top!, use a simple loop + `array_merge_recursive` is less code, easier to maintain and faster, too (cf my answer) – Elias Van Ootegem Dec 21 '13 at 16:54
  • 1
    It's very interesting how many different ways of using array functions for producing the same result exist :) – ComFreek Dec 21 '13 at 17:04
  • In addition: looking at your `array_map` + `array_keys` + `call_user_func_array` & `array_merge` bit, which assigns `$keys`, I think you could add an `array_unique` call there. Seriously: getting the keys takes you 5 different functions (where one is a lambda => instance of Closure class!), and `array_merge` is called N times, where N == count($array). Don't you think that's a bit much. Again: compare it to `foreach` (= languange construct) + N calls to `array_merge_recursive`. Which looks like the cleaner alternateive to you? – Elias Van Ootegem Dec 21 '13 at 17:05
  • For loops are so passé, real men use functions! Good shout with the array_unique. Single call to array_merge_recursive vs n calls... It supports multiple array arguments, why not use that. – Stuart Wakefield Dec 21 '13 at 17:10
  • I've added your array_unique suggestion. P.S small thing, with a single value array_merge_recursive does not put the values in an array as the OP showed. – Stuart Wakefield Dec 21 '13 at 17:12
  • P.S. For less code, rather than use a for loop simply do this in your solution: `$result = call_user_func_array('array_merge_recursive', $arr);`... Done – Stuart Wakefield Dec 21 '13 at 17:15
  • And another thing! "array_merge is called N times, where N == count($array)" is incorrect, the array is applied as the arguments to array_merge. It is called only once. – Stuart Wakefield Dec 21 '13 at 17:18
-1

Try using array_merge_recursive OR array_column