-1

I have a PHP array that looks like this:

Array
(
    [0] => Array
        (
            [id] => 2
            [name] => Item2
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [name] => Item1
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 5
                                            [name] => Item5
                                        )
                                )
                        )
                    [1] => Array
                        (
                            [id] => 4
                            [name] => Item4
                        )
                )
        )
    [1] => Array
        (
            [id] => 3
            [name] => Item3
        )
)

It has unknown (unpredictable) depth and length. Any item on any level may or may not have children. It has been created from an xml file that contains product groups. I would like to convert it to an array that contains arrays of three elements: id, name, and parent id:

[0] => array('id' => '2', 'name' => 'Item2', 'parent' => 0),
[1] => array('id' => '1', 'name' => 'Item1', 'parent' => 2),
[2] => array('id' => '4', 'name' => 'Item4', 'parent' => 2) etc.

How can I do it? Thank you!

unclexo
  • 3,691
  • 2
  • 18
  • 26
Artem
  • 81
  • 6
  • I’m voting to close this question because this reads like a _"who can do this for me"_ question. You're expected to describe the problem, and more importantly: *show what you've tried* – Elias Van Ootegem Apr 21 '20 at 08:49
  • Thanks for pointing this out, Elias. I used for and foreach cycles, but felt it was dumb to include the solutions that don't work. Also, I found no questions like this on the web, so thought my question might help someone else who would have the same kind of problem. This was my first queston on Stackoverflow, so I hope to make my questions better next time. – Artem Apr 21 '20 at 10:57

1 Answers1

2

This can be achieved with a recursive function that pushes all the items from a given level of the array, then calls itself for any children arrays:

function list_items($array, $parent = 0) {
    $output = array();
    foreach ($array as $arr) {
        $output[] = array('id' => $arr['id'], 'name' => $arr['name'], 'parent' => $parent);
        if (is_array($arr['children'] ?? NULL)) {
            $output = array_merge($output, list_items($arr['children'], $arr['id']));
        }
    }
    return $output;
}

$items = list_items($array);

Output (for my slightly expanded data):

Array
(
    [0] => Array
        (
            [id] => 2
            [name] => Item2
            [parent] => 0
        )
    [1] => Array
        (
            [id] => 1
            [name] => Item1
            [parent] => 2
        )
    [2] => Array
        (
            [id] => 5
            [name] => Item5
            [parent] => 1
        )
    [3] => Array
        (
            [id] => 4
            [name] => Item4
            [parent] => 2
        )
    [4] => Array
        (
            [id] => 3
            [name] => Item3
            [parent] => 0
        )
)

Demo on 3v4l.org

Update

It turns out that there is an inconsistency in the array structure; when there is only one child, only the child value is stored rather than a single element array. This can be dealt with by checking the the array to see if the id (Ид) element is set, and if it is, pushing the array one level deeper before processing:

function list_items($array, $parent = 0) {
    $output = array();
    if (isset($array['Ид'])) {
        $array = array($array);
    }
    foreach ($array as $arr) {
        if (!is_array($arr)) echo $arr;
        $output[] = array('id' => $arr['Ид'], 'name' => $arr['Наименование'], 'parent' => $parent);
        if (is_array($arr['Группы']['Группа'] ?? NULL)) {
            $output = array_merge($output, list_items($arr['Группы']['Группа'], $arr['Ид']));
        }
    }
    return $output;
}

$items = list_items($array);
print_r($items);

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
  • Excellent! Thank you Nick! There's something I don't understand - some output names are with errors: one name was "6. Item name", and it gave me just 6, another was "Журналы учета" (all names are Cyrillic, though), and it gave me a strange symbol Also I got a child element with a letter 'f' for both name and id, as a second child of some parent, while the parent has one child in the original array. – Artem Apr 20 '20 at 05:29
  • @tolkodelo weird. Can you perhaps modify my demo with data which shows the problem? Then I could have a go at fixing it. – Nick Apr 20 '20 at 05:30
  • @tolkodelo those names in and of themselves are not the issue: https://3v4l.org/Qh28c Are you sure the array is properly formed? – Nick Apr 20 '20 at 05:32
  • I tried to add the whole array into your example, but is reads, "413 Request entity too large". Wanted to try it in real conditions, so I didn't shorten it. I've made the array a file and uploaded to https://yadi.sk/d/vPiqZkRsWQqw_A Opened it in phpstorm, it found no errors, so the array must be properly formed. – Artem Apr 20 '20 at 06:17
  • I get an error if I try to download that file ("can't download that file now"). Can you try pastebin? – Nick Apr 20 '20 at 06:19
  • here you are https://pastebin.com/jjciC6JD – Artem Apr 20 '20 at 06:22
  • @tolkodelo I can't read Russian! (I presume?) Which are the `id`, `name` and `children` fields? – Nick Apr 20 '20 at 06:23
  • note that your example needs to be modified slightly, as the children array actually contains a sub array called Группа, so $output = array_merge($output, list_items($arr['Группы']['Группа'], $arr['Ид'])); – Artem Apr 20 '20 at 06:25
  • Whoops) Sorry ) Id = Ид, name = Наименование, children array = Группы, and that has one child, a sub array Группа, which contains actual items – Artem Apr 20 '20 at 06:27
  • @tolkodelo It's your job. I mean to adapt the array keys to your language. But do not suggest to modify the answer. – unclexo Apr 20 '20 at 06:28
  • @Nick here you are https://3v4l.org/HfH8DZ – Artem Apr 20 '20 at 06:33
  • @tolkodelo yeah I have it working on my server. There's a couple of errors, I'll take a look at them shortly. – Nick Apr 20 '20 at 06:34
  • @unclexo not sure I understand you. I had my array names in Cyrillic, but I changed them to English, like ID and Name, for everyone to better understand the question. Now that we discuss the actial array, I gave Nick a clue what its keys mean. – Artem Apr 20 '20 at 06:36
  • @tolkodelo it's ok dear! But we're here to guide you that you can do this that way! It may happen to me! Nick's answer is enough to handle your array. Now it's your turn to make it work. And I think it would be better for you. :) – unclexo Apr 20 '20 at 06:47
  • Strange.... If I replace the item's name 'Журналы учета' with 'somename', the name in the output array is 's'. The cause must be not the Cyrillic letters then – Artem Apr 20 '20 at 06:51
  • @tolkodelo there's a couple of places where your array structure is inconsistent; when there is only one child there is just the child value, instead of a single element array. See my edit, that should fix it (I tried it with your pastebin array and got an output of 81 elements which all look reasonable). – Nick Apr 20 '20 at 07:19
  • @Nick I can see it now! Thank you so much! – Artem Apr 20 '20 at 10:51