如何在保持结构完整的同时按路径过滤数组和对象

时间:2018-12-20 20:26:13

标签: php arrays object

我正在开发一个在PHP 7.2上运行的JSON API。 API的功能之一是基本的“仅向我显示这些列”过滤器。我希望能够在无需手动处理路径中每个“级别”的情况下使其动态化。

我的第一种方法是仅支持五个深度的嵌套路径。这涵盖了大多数用例,并且已经运行了好几年。

/ api / request / stuff?column = col1-sub1-data2,col1-sub2

完整结构(可以是对象或数组,但如有必要,我可以强制转换为数组):

[
  'col1' => [
    'sub1' => [
      'data1' => false,
      'data2' => 'abc',
      'data3' => 123
    ],
    'sub2' => [
      'other1' => true,
      'data3' => 987
    ]
  ],
  'col2' => [
  ]
]

这是我非常幼稚的方法的一个例子:

foreach ($columns as $column) {
    $path = explode('-', $column);

    if (!is_array($path) || empty($path)) {
        continue;
    }

    $pathCount = count($path);

    switch ($pathCount) {
        case 0:
        case 1:
            /* Too shallow. These should be handled before this point anyway... */
            continue 2;
        case 2:
            if (!isset($detailObject[ $path[ 0 ] ][ $path[ 1 ] ])
            ) {
                continue 2;
            }

            $newArray[ $path[ 0 ] ][ $path[ 1 ] ] = $detailObject[ $path[ 0 ] ][ $path[ 1 ] ];
            break;
        case 3:
            if (!isset($detailObject[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ])
            ) {
                continue 2;
            }

            $newArray[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ] = $detailObject[ $path[ 0 ] ][ $path[ 1 ] ][ $path[ 2 ] ];
            break;
    }
}

API使用column参数处理数据并返回以下内容:

[
  'col1' => [
    'sub1' => [
      'data2' => 'abc'
    ],
    'sub2' => [
      'other1' => true,
      'data3' => 987
    ]
]

有没有办法动态产生此输出?

1 个答案:

答案 0 :(得分:0)

对任意深度的嵌套结构执行操作几乎总是需要递归函数。

function filterPath($data, $path) {
    $key = array_shift($path);
    if( empty($path) ) {
        return [$key => $data[$key]];
    } else {
        return [$key => filterPath($data[$key], $path) ];
    }
}

function filterPaths($data, $paths) {
    $out = [];
    foreach($paths as $path) {
        $out = array_merge_recursive($out, filterPath($data, $path));
    }
    return $out;
}

$data = [
  'col1' => [
    'sub1' => [
      'data1' => false,
      'data2' => 'abc',
      'data3' => 123
    ],
    'sub2' => [
      'other1' => true,
      'data3' => 987
    ]
  ],
  'col2' => [
  ]
];

var_dump(
    filterPaths($data, [
        ['col1', 'sub1', 'data1'],
        ['col1', 'sub1', 'data3'],
        ['col1', 'sub2'],
        ['col2']
    ])
);

输出:

array(2) {
  ["col1"]=> array(2) {
    ["sub1"]=> array(2) {
      ["data1"]=> bool(false)
      ["data3"]=> int(123)
    }
    ["sub2"]=> array(2) {
      ["other1"]=> bool(true)
      ["data3"]=> int(987)
    }
  }
  ["col2"]=> array(0) {}
}