1

I'm in PHP and I've got an array that looks like this. A single dimension array whose keys are bracketed strings.

array(
    'matrix[min_rows]' => '0',
    'matrix[max_rows]' => '',
    'matrix[col_order][]' => 'col_new_1',
    'matrix[cols][col_new_0][type]' => 'text',
    'matrix[cols][col_new_1][type]' => 'text',
    'matrix[cols][col_new_0][label]' => 'Cell 1',
    'matrix[cols][col_new_1][label]' => 'Cell 2',
    'matrix[cols][col_new_0][name]' => 'cell_1',
    'matrix[cols][col_new_1][name]' => 'cell_2',
    'matrix[cols][col_new_0][instructions]' => '',
    'matrix[cols][col_new_1][instructions]' => '',
    'matrix[cols][col_new_0][width]' => '33%',
    'matrix[cols][col_new_1][width]' => '',
    'matrix[cols][col_new_0][settings][maxl]' => '',
    'matrix[cols][col_new_0][settings][fmt]' => 'none',
    'matrix[cols][col_new_0][settings][content]' => 'all',
    'matrix[cols][col_new_1][settings][maxl]' => '140',
    'matrix[cols][col_new_1][settings][multiline]' => 'y',
    'matrix[cols][col_new_1][settings][fmt]' => 'none',
    'matrix[cols][col_new_1][settings][content]' => 'all',
)

Is there any easy way to convert that to a normal nested array, ie:

array(
    'matrix' => array(
        'min_rows' => '0',
        'max_rows' => '',
        'col_order' => array('col_new_1'),
        'cols' => array(
            'col_new_0' => array(
                'type' => 'text',
                'label' => 'Cell 1',
....etc....

This is my current solution, but I was wondering if there's something more native or efficient:

foreach ($decoded_field_type_settings as $key => $value)
{
    if (preg_match_all('/\[(.*?)\]/', $key, $matches))
    {
        $new_key = substr($key, 0, strpos($key, '['));

        if ( ! isset($field_type_settings[$new_key]))
        {
            $field_type_settings[$new_key] = array();
        }

        $array =& $field_type_settings[$new_key];

        $count = count($matches[1]) - 1;

        foreach ($matches[1] as $i => $sub_key)
        {
            if ( ! $sub_key)
            {
                if ($i < $count)
                {
                    $array[] = array();
                }
                else
                {
                    $array[] = $value;
                }
            }
            else
            {
                if ( ! isset($array[$sub_key]))
                {
                    if ($i < $count)
                    {
                        $array[$sub_key] = array();
                    }
                    else
                    {
                        $array[$sub_key] = $value;
                    }
                }
            }

            if ($i < $count)
            {
                $array =& $array[$sub_key];
            }
        }
    }
    else
    {
        $field_type_settings[$key] = $value;
    }
}

UPDATE: I posted an answer below.

3
  • 1
    what's the source of the original data? Commented Nov 17, 2011 at 21:50
  • It's a hidden form field that's populated by jQuery.serializeArray(). I'm piggybacking on a third party app, so I can't change this data. Commented Nov 17, 2011 at 22:00
  • I was just about to post an answer using http_build_query() and parse_str() but did a re-read of your question and discovered you already have this answer. The trouble is, you didn't post your solution as an Answer. Please edit your question to remove the solution, post your solution as an answer, and award it the green tick (this way your question is deemed resolved). ...oh, you haven't been on site for 6 years. Commented Dec 21, 2017 at 14:46

5 Answers 5

1

This might work, although it would probably generate some warnings:

$matrix = array();
foreach($arr as $key => $value) {
   eval('$' . $key . ' = \'' . $value . '\';');
}

var_dump($matrix);
Sign up to request clarification or add additional context in comments.

4 Comments

I was about to propose that idea. Apart from doing a mega tokenizing job, this is the only valid idea i can think of.. Ugly, but works...
I thought of eval too, although I've generally heard you should avoid that for security purposes, etc... is that something to consider here? (Maybe you could at least make sure the string has no semicolons or $ signs or something...?)
Problems may occur if the value stored in $value contains quotes, backslashes, dollar signs... Plus, running your solution gave a lot of 'undefined constant' notices.
@Frosty - agreed, based my solution on the sample data he gave. And regarding the notices, that's why I mentioned that it would probably generate warnings
0

I think this should do it...

<?php
function convert2dTo3d($source) {
    $refs = array();
    $output = array();

    foreach ($source AS $key => $val) {
        $tok = strtok($key, '[]');

        $prev_tok = NULL;
        while ($tok !== FALSE) {
            $this_ref =& $refs[$tok];

            if ($prev_tok === NULL)
                $output[$tok] =& $this_ref;
            else
                $refs[$prev_tok][$tok] =& $this_ref;

            $prev_tok = $tok;
            $tok = strtok('[]');

            if ($tok === FALSE)
                $refs[$prev_tok] = $val;
        }
    }

    return $output;
}


// Test
$source = array(
    'matrix[min_rows]' => '0',
    'matrix[max_rows]' => '',
    'matrix[col_order][]' => 'col_new_1',
    'matrix[cols][col_new_0][type]' => 'text',
    'matrix[cols][col_new_1][type]' => 'text',
    'matrix[cols][col_new_0][label]' => 'Cell 1',
    'matrix[cols][col_new_1][label]' => 'Cell 2',
    'matrix[cols][col_new_0][name]' => 'cell_1',
    'matrix[cols][col_new_1][name]' => 'cell_2',
    'matrix[cols][col_new_0][instructions]' => '',
    'matrix[cols][col_new_1][instructions]' => '',
    'matrix[cols][col_new_0][width]' => '33%',
    'matrix[cols][col_new_1][width]' => '',
    'matrix[cols][col_new_0][settings][maxl]' => '',
    'matrix[cols][col_new_0][settings][fmt]' => 'none',
    'matrix[cols][col_new_0][settings][content]' => 'all',
    'matrix[cols][col_new_1][settings][maxl]' => '140',
    'matrix[cols][col_new_1][settings][multiline]' => 'y',
    'matrix[cols][col_new_1][settings][fmt]' => 'none',
    'matrix[cols][col_new_1][settings][content]' => 'all',
);

echo "<pre>";
print_r(convert2dTo3d($source));
echo "</pre>";

Comments

0

It seems that the OP wants, as output, an array declaration which can be parsed directly by PHP. So I suggest to use var_export().

$array = array(
    'matrix[min_rows]' => '0',
    // ......
    // ......
    // ......
    'matrix[cols][col_new_1][settings][content]' => 'all'
);

$matrix = array();

foreach ($array as $key => $value)
{
  // fix missing quotes around array indexes
  $key = str_replace(array("[", "]", "['']"), array("['", "']", "[]"), $key);
  // fill PHP array
  eval('$'.$key.' = $value;');
}

var_export($matrix);

Comments

0

You can make use of a simple, regular expression based parser that creates a multidimensional array based on the information stored in the string per each key:

function parse_flat_matrix(array $flat)
{
    $matrix = array();
    $varname = 'matrix';
    $nameToken = '[a-z0-9_]*';

    foreach($flat as $key => $value)
    {
        $keys = preg_split(sprintf('/(\[%s\])/', $nameToken), $key, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

        if ($varname !== array_shift($keys))
        {
            throw new InvalidArgumentException(sprintf('Invalid key %s.', $key));
        }

        $p =& $matrix;
        foreach($keys as $k)
        {
            $r = preg_match(sprintf('/^\[(%s)\]$/', $nameToken), $k, $kk);
            if (!$r)
            {
                throw new InvalidArgumentException(sprintf('Invalid subkey %s in key %s.', $k, $key));
            }

            if ('' === $kk[1])
            {
                $p =& $p[];
            }
            else
            {
                $p =& $p[$kk[1]];
            }
        }
        $p = $value;
        unset($p);
    }

    return $matrix;
}

With your example data given, it will create this array:

Array
(
    [min_rows] => 0
    [max_rows] => 
    [col_order] => Array
        (
            [0] => col_new_1
        )

    [cols] => Array
        (
            [col_new_0] => Array
                (
                    [type] => text
                    [label] => Cell 1
                    [name] => cell_1
                    [instructions] => 
                    [width] => 33%
                    [settings] => Array
                        (
                            [maxl] => 
                            [fmt] => none
                            [content] => all
                        )

                )

            [col_new_1] => Array
                (
                    [type] => text
                    [label] => Cell 2
                    [name] => cell_2
                    [instructions] => 
                    [width] => 
                    [settings] => Array
                        (
                            [maxl] => 140
                            [multiline] => y
                            [fmt] => none
                            [content] => all
                        )

                )

        )

)

Comments

0

This was the simplest solution, involving no regex parsing:

$original_array = array(
    'matrix[min_rows]' => '0',
    'matrix[max_rows]' => '',
    'matrix[col_order][]' => 'col_new_1',
    'matrix[cols][col_new_0][type]' => 'text',
    'matrix[cols][col_new_1][type]' => 'text',
    'matrix[cols][col_new_0][label]' => 'Cell 1',
    'matrix[cols][col_new_1][label]' => 'Cell 2',
    'matrix[cols][col_new_0][name]' => 'cell_1',
    'matrix[cols][col_new_1][name]' => 'cell_2',
    'matrix[cols][col_new_0][instructions]' => '',
    'matrix[cols][col_new_1][instructions]' => '',
    'matrix[cols][col_new_0][width]' => '33%',
    'matrix[cols][col_new_1][width]' => '',
    'matrix[cols][col_new_0][settings][maxl]' => '',
    'matrix[cols][col_new_0][settings][fmt]' => 'none',
    'matrix[cols][col_new_0][settings][content]' => 'all',
    'matrix[cols][col_new_1][settings][maxl]' => '140',
    'matrix[cols][col_new_1][settings][multiline]' => 'y',
    'matrix[cols][col_new_1][settings][fmt]' => 'none',
    'matrix[cols][col_new_1][settings][content]' => 'all',
);

$query_string = http_build_query($original_array);

$final_array = array();

parse_str($query_string, $final_array);

var_dump($final_array);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.