【问题标题】:Transforming a multidimensional array in PHP在 PHP 中转换多维数组
【发布时间】:2020-05-12 18:00:54
【问题描述】:

我有一个具有某些结构的数组,必须递归地转换为新结构。请考虑以下示例:

<?php

class Foo {

    public function getData()
    {
        return [
            'a' => [
                'fooa1' => 'bara1',
                'fooa2' => 'bara2',
                'fooa3' => 'bara3',
                'parent' => '0'
            ],
            'b' => [
                'foob1' => 'barb1',
                'foob2' => 'barb2',
                'foob3' => 'barb3',
                'parent' => '0'
            ],
            'c' => [
                'fooc1' => 'barc1',
                'fooc2' => 'barc2',
                'fooc3' => 'barc3',
                'parent' => 'b'
            ],
            'd' => [
                'food1' => 'bard1',
                'food2' => 'bard2',
                'food3' => 'bard3',
                'parent' => 'a'
            ],
            'e' => [
                'fooe1' => 'bare1',
                'fooe2' => 'bare2',
                'fooe3' => 'bare3',
                'parent' => 'c'
            ],
        ];
    }

    public function getDataPattern()
    {
        return [
            'a' => [
                'fooa1' => 'bara1',
                'fooa2' => 'bara2',
                'fooa3' => 'bara3',
                'children' => [
                    'd' => [
                        'food1' => 'bard1',
                        'food2' => 'bard2',
                        'food3' => 'bard3',
                        'parent' => 'a'
                    ],
                ]
            ],
            'b' => [
                'foob1' => 'barb1',
                'foob2' => 'barb2',
                'foob3' => 'barb3',
                'children' => [
                    'c' => [
                        'fooc1' => 'barc1',
                        'fooc2' => 'barc2',
                        'fooc3' => 'barc3',
                        'parent' => 'b',
                        'children' => [
                            'e' => [
                                'fooe1' => 'bare1',
                                'fooe2' => 'bare2',
                                'fooe3' => 'bare3',
                                'parent' => 'c'
                            ],
                        ]
                    ],
                ]
            ],
        ];
    }

    // DOESN'T WORK YET :(
    public function transformArrayToPattern()
    {
        $recordsArray = $this->getData();

        foreach ($recordsArray as $key => $recordArray) {
            if ($recordArray['parent'] !== '0') {
                $recordsArray[$key]['children'] = $recordArray;
                $this->transformArrayToPattern($recordArray);
            }
        }
        return $recordsArray;
    }
}

级别和嵌套可以变化,但基本上它是输入上的平面结构,我最终需要这样的嵌套结构。

你有什么想法,怎么做?谢谢!

编辑:对不起!我需要更正这个例子,因为显然我们只使用数字 ID 而不是字符串。正确的格式是这样的:

    public function getData()
    {
        return [
            1 => [
                'fooa1' => 'bara1',
                'fooa2' => 'bara2',
                'fooa3' => 'bara3',
                'parent' => 0
            ],
            2 => [
                'food1' => 'bard1',
                'food2' => 'bard2',
                'food3' => 'bard3',
                'parent' => 1
            ],
            3 => [
                'fooc1' => 'barc1',
                'fooc2' => 'barc2',
                'fooc3' => 'barc3',
                'parent' => 1
            ],
        ];
    }

...但是到目前为止,这两种解决方案都无法解决。您介意调整数字键的脚本吗?非常感谢!

【问题讨论】:

  • 有必要用你的方法吗?还是可以手动完成?
  • getData()getDataPattern() 方法只是为了举例。主要的转换逻辑是最重要的部分,应该发生在函数transformArrayToPattern()中。
  • 并不是说这会解决所有问题,您在if ($recordArray['parent'] !== 0) 有一个错误,... 0 上缺少引号。

标签: php arrays recursion multidimensional-array


【解决方案1】:

我认为您最好的选择是将您的数据数组转换为包含更多对象属性的对象,因为对象总是通过引用传递 - 这意味着为您处理递归。

将关联的嵌套数组转换为对象的一种简写方式是使用json_decode(json_encode($data))

因此,您可以使用 transformArrayToPattern() 方法执行以下操作:

public function transformArrayToPattern()
{
    $recordsObject = json_decode(json_encode($this->getData()));

    foreach ($recordsObject as $key => $obj) {
        if ($obj->parent !== 0) {
            $recordsObject->{$obj->parent}->children[$key] = $obj;
        }
    }

    return array_filter((array) $recordsObject, function ($record) {
        return $record->parent === 0;
    });
}

这基本上是将父对象的引用添加到类的children 属性,并提供您正在寻找的嵌套。 https://www.php.net/manual/en/language.oop5.references.php

输出https://3v4l.org/3flcp

【讨论】:

  • 这与 OP 想要的输出不匹配。
  • 刚刚编辑,因为我意识到我将父母添加为孩子,而不是正确的方式
  • 还是不符合。有关预期输出,请参阅 getDataPattern() 方法。
  • 出于测试目的,我更换了父母。如果他们需要它作为一个数组,他们可以很容易地再次将其转换回来。
  • 是的,抱歉,我现在已经更改了。我也没有注意到不应该在顶层返回带有父母的元素。添加了一个过滤器 - 谢谢!
【解决方案2】:
<?php


class Foo {

    public function getData()
    {
        return [
            'a' => [
                'fooa1' => 'bara1',
                'fooa2' => 'bara2',
                'fooa3' => 'bara3',
                'parent' => '0'
            ],
            'b' => [
                'foob1' => 'barb1',
                'foob2' => 'barb2',
                'foob3' => 'barb3',
                'parent' => '0'
            ],
            'c' => [
                'fooc1' => 'barc1',
                'fooc2' => 'barc2',
                'fooc3' => 'barc3',
                'parent' => 'b'
            ],
            'd' => [
                'food1' => 'bard1',
                'food2' => 'bard2',
                'food3' => 'bard3',
                'parent' => 'a'
            ],
            'e' => [
                'fooe1' => 'bare1',
                'fooe2' => 'bare2',
                'fooe3' => 'bare3',
                'parent' => 'c'
            ],
        ];
    }

    public function getDataPattern()
    {
        return [
            'a' => [
                'fooa1' => 'bara1',
                'fooa2' => 'bara2',
                'fooa3' => 'bara3',
                'children' => [
                    'd' => [
                        'food1' => 'bard1',
                        'food2' => 'bard2',
                        'food3' => 'bard3',
                        'parent' => 'a'
                    ],
                ]
            ],
            'b' => [
                'foob1' => 'barb1',
                'foob2' => 'barb2',
                'foob3' => 'barb3',
                'children' => [
                    'c' => [
                        'fooc1' => 'barc1',
                        'fooc2' => 'barc2',
                        'fooc3' => 'barc3',
                        'parent' => 'b',
                        'children' => [
                            'e' => [
                                'fooe1' => 'bare1',
                                'fooe2' => 'bare2',
                                'fooe3' => 'bare3',
                                'parent' => 'c'
                            ],
                        ]
                    ],
                ]
            ],
        ];
    }

    public function transformArrayToPattern(){
        $data = $this->getData();
        $nodes = [];
        foreach($data as $node_name => $node_data){
            if(!isset($nodes[$node_data['parent']])) $nodes[$node_data['parent']] = [];
            $nodes[$node_data['parent']][] = $node_name;
        }

        //print_r($nodes);

        $result = [];

        foreach($nodes['0'] as $node_name){
            $result[$node_name] = $this->dfs($node_name,$nodes,$data);
            unset($result[$node_name]['parent']); // to unset parent 0
        }

        return $result;
    }

    private function dfs($node_name,$nodes,$data){
        if(!isset($nodes[$node_name])){
            return $data[$node_name];
        }

        $children = [];
        foreach($nodes[$node_name] as $kid_name){
            $children[$kid_name] = $this->dfs($kid_name,$nodes,$data);
        }

        return array_merge($data[$node_name],['children' => $children]);
    }
}


$o = new Foo();

print_r($o->transformArrayToPattern());

演示: https://3v4l.org/VLcgK

算法:

  • 嗯,您必须首先具有节点层次结构才能进行递归调用。
  • 我们在第一步中执行此操作,并将所有层次结构存储在 $nodes 中。
  • 现在,我们只对具有父 0 的元素进行迭代,因为我们将递归地访问它的子节点,因此我们始终可以确保收集所有有问题的节点(数据)。
  • 如果某个子节点没有任何父节点(意味着在$nodes 中不存在),那么无论如何它都是叶子节点。因此,只需返回它的数据,而无需在 dfs() 方法中进一步递归。这里,dfs() 基本上代表depth first search

【讨论】:

  • 非常感谢!请您看一下,为什么它不能处理我上次编辑中更正的数字数组键?
  • @QBnewmedia 它似乎对我有用3v4l.org/qaVBW
  • 谢谢,它确实有效。但是,如果数组从 0 开始而不是像我的示例中那样从 1 开始,它就会失败。我修复了数组的起始索引,所以现在可以正常工作了。
  • @QBnewmedia 很高兴为您提供帮助,但您能否演示一下哪个第 0 个索引使其失败?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
  • 1970-01-01
相关资源
最近更新 更多