1

This is an example of array I have:

 $data = [
      'total_amount' => 200,
      'purchase_date' => '01.01.2020',
      'items' => [
           [
                'name' => 'T-shirt',
                'price' => 50
           ],
           [
                'name' => 'Jacket',
                'price' => 150
           ],
       ]
];

and I would like to get something like this:

$data = [
    [
        'k' => 'total_amount',
        'v' => 200
    ],
    [
        'k' => 'purchase_date',
        'v' => '01.01.2020'
    ]
    [
        'k' => 'items',
        'v' => [
           [
                [
                    'k' => 'name',
                    'v' => 'T-Shirt'
                ],
                [
                    'k' => 'price',
                    'v' => 50
                ]
           ],
           [
                [
                    'k' => 'name',
                    'v' => 'Jacket'
                ],
                [
                    'k' => 'price',
                    'v' => 150
                ]
           ]
        ]
    ]
]

It's not a big problem to parse the first array and then create desired output. Also, if we have nested and nasted and nested array, then I just use a recursion and it seems to work pretty good. Here is the code I have:

public function convert(array $data) : array
{
    $output = [];

    foreach ($data as $k => $v) {
        if (is_array($v)) {
            $output[] = ['k' => $k, 'v' => $this->value($v)];
        } else {
            $output[] = ['k' => $k, 'v' => $v];
        }
    }

    return $output;
}

and the following:

   protected function value($items)
{
    $output = [];
    $i = 0;

    foreach ($items as $itemK => $itemV) {
        if (!is_array($itemV)) {
            $output[$i] = ['k' => $itemK, 'v' => $itemV];
            continue;
        }

        foreach ($itemV as $k => $v) {
            if (is_array($v)) {
                $output[$i][] = ['k' => $k, 'v' => $this->value($v)];
                continue;
            }

            $output[$i][] = ['k' => $k, 'v' => $v];
        }

        $i++;
    }

    return $output;
}

The question is if there is a way to optimize this code without using too many foreach functions (maybe there is built-in PHP function that I can leverage) and maybe avoid recursion?

2 Answers 2

1

To tighten-up / D.R.Y.-out your code, identify the processes that are written more than once and try to refactor the script in a way that will permit the single declaration of a function call or process.

  1. You know that you will need to use recursion when $value is an array, so conditionally call&cache the recursion's return value to a variable.
  2. You know that you will need need to push the new associative structure into the output array when the key is not an index, so conditionally push what is needed.

Using the following, you get your desired output without redundant scripting.

Code: (Demo)

function restructure($array) {
    $output = [];
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $value = restructure($value);
        }
        $output[] = is_int($key) ? $value : ['k' => $key, 'v' => $value];
    }
    return $output;
}

var_export(restructure($data));
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your support. This is great and the cleanest way to achieve desired.
1

Here's a slightly simplified version of your code. Note that if you want to allow arbitrarily nested key/value pairs, recursion is the only effective method to do that:

function convert($array) {
    $output = array();
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            // nested array with numeric keys? if so don't create a k,v pair
            if (is_numeric($key)) {
                $output[] = convert($value);
            }
            else {
                $output[] = array('k' => $key, 'v' => convert($value));
            }
        }
        else {
            $output[] = array('k' => $key, 'v' => $value);
        }
    }
    return $output;
}

Output:

Array
(
    [0] => Array
        (
            [k] => total_amount
            [v] => 200
        )
    [1] => Array
        (
            [k] => purchase_date
            [v] => 01.01.2020
        )
    [2] => Array
        (
            [k] => items
            [v] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [k] => name
                                    [v] => T-shirt
                                )
                            [1] => Array
                                (
                                    [k] => price
                                    [v] => 50
                                )
                        )
                    [1] => Array
                        (
                            [0] => Array
                                (
                                    [k] => name
                                    [v] => Jacket
                                )
                            [1] => Array
                                (
                                    [k] => price
                                    [v] => 150
                                )
                        )
                )
        )
)

Demo on 3v4l.org

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.