Question

I have a multidimensional array of form data thats an been unserialized from YAML. As such it looks something like this:

Array(
  'name' => 'Somone',
  'email' => 'someone@none.local',
  'billing' => Array(
     'address_1' => '1234 Somewhere'
     'address_2' => NULL,
     'city' => 'Somewhere',
     'state' => 'ST'
     'country' => 'CO'
     'postal_code' => '12345'
   ),
  'shipping' => Array(
     'address_1' => '1234 Somewhere'
     'address_2' => NULL,
     'city' => 'Somewhere',
     'state' => 'ST'
     'country' => 'CO'
     'postal_code' => '12345'
  )
);

What i need to do is to flatten this so I can output some CSV, so it should look something like this:

Array(
  'name' => 'Somone',
  'email' => 'someone@none.local',
  'billing_address_1' => '1234 Somewhere'
  'billing_address_2' => NULL,
  'billing_city' => 'Somewhere',
  'billing_state' => 'ST'
  'billing_country' => 'CO'
  'billing_postal_code' => '12345'
  'shipping_address_1' => '1234 Somewhere'
  'shipping_address_2' => NULL,
  'shipping_city' => 'Somewhere',
  'shipping_state' => 'ST'
  'shipping_country' => 'CO'
  'shipping_postal_code' => '12345'
);

I will never know for sure how deep the array/hash is - it coul be only 2 levels as shown here or it could be 5.

Also this is in Symfony 1.4 so the sfForm with all of its luxuries is available if needed. I'm thinking there should be a sensible way to do this using the widget schema(s) and widgets. However, i would like to avoid binding the data back to the form if possible. This isn't part of the actual form submission process but is completely separate in an action for admins to download sets of the submitted data.

Was it helpful?

Solution

Just a quick-hack but it works pretty good:

function array_flatten($array, $prefix = '') {
    $newArray = array();
    foreach($array as $key => $value) {
        if(is_array($value)) {
                $newArray = array_merge($newArray, array_flatten($value, $key));
        }
        else {
                $index = empty($prefix) ? $key : $prefix.'_'.$key;
                $newArray[$index] = $value;
             }
     }
     return $newArray;
}

Test:

$a = array(
        "a" => "b",
        "ca" => array(
            "de" => "ef",
            "ef" => "gd"
        )
);

var_dump(array_flatten($a));

// Output:
/*

array(3) {
  ["a"]=>
  string(1) "b"
  ["ca_de"]=>
  string(2) "ef"
  ["ca_ef"]=>
  string(2) "gd"
}

*/

OTHER TIPS

function flatten(Array $array, $name = '') {
    $ret = array();

    foreach ($array as $key => $value) {
        $itemname = ($name ? $name . '_' : '') . $key;

        if (is_array($value)) {
            $ret = array_merge($ret, flatten($value, $itemname));
        } else {
            $ret[$itemname] = $value;
        }
    }

    return $ret;
}

How about this? I didn't know how you wanted to handle duplicate keys, so I left that option up to you. Just replace ; // Do something here on duplicate key with your own code.

$info = Array(
    'name' => 'Someone',
    'email' => 'someone@none.local',
    'billing' => Array(
        'address_1' => '1234 Somewhere',
        'address_2' => NULL,
        'city' => 'Somewhere',
        'state' => 'ST',
        'country' => 'CO',
        'postal_code' => '12345'
    ),
    'shipping' => Array(
        'address_1' => '1234 Somewhere',
        'address_2' => NULL,
        'city' => 'Somewhere',
        'state' => 'ST',
        'country' => 'CO',
        'postal_code' => '12345'
    )
);

function explodeArray($array, &$data, $prefix = "") {
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            explodeArray($value, $data, $prefix . $key . "_");
        } else {
            if (!array_key_exists($prefix . $key, $data)) {
                $data[$prefix . $key] = $value;
            } else {
                ; // Do something here on duplicate key
            }
        }
    }
}

$result = array();
explodeArray($info, $result);

print_r($result);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top