从未知深度多维创建一个新数组并保持相同的结构

Create a new array from a unknown depth multidimensional and keep the same structure

我有一个可以有任意深度的多维数组。我想做的是根据 动态键 过滤整个路径并创建一个新数组。

数组示例

$originalArray = [
    "title" => "BACKPACK MULTICOLOUR",
    "description" => "description here",
    "images" => [
        [
            "id" => 12323123123,
            "width" => 635,
            "height" => 560,
            "src" => "https://example.com",
            "variant_ids": [32694976315473, 32863017926737],
        ],
        [
            "id" => 4365656656565,
            "width" => 635,
            "height" => 560,
            "src" => "https://example.com",
            "variant_ids": [32694976315473, 32863017926737],
        ]
    ],
    "price" => [
        "normal" => 11.00,
        "discount" => [
            "gold_members" => 9.00,
            "silver_members" => 10.00,
            "bronze_members" => null
        ]
    ]
];

过滤掉键“title, width, height, gold_members”后输出的示例。只有数组树末尾的键应该是有效的,所以当 images 在 filter

中时什么也不会发生
$newArray = [
    "title" => "BACKPACK MULTICOLOUR",
    "images" => [
        [
            "width" => 635,
            "height" => 560,
        ],
        [
            "width" => 635,
            "height" => 560,
        ]
    ],
    "price" => [
        "discount" => [
            "gold_members" => 9.00,
        ]
    ]
];

我想我应该创建一个循环遍历每个元素的函数,当它是一个关联数组时,它应该再次调用自己

因为过滤后的路径未知,所以我无法像这样进行硬编码 setter:

$newArray["images"][0]["width"] = 635

以下过滤器将作为示例,但它基本上应该是动态的

例如我现在拥有的:

$newArray = handleArray($originalArray);
    

handleArray($array) 
{
    $filter = ["title", "width", "height", "gold_members"];

    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $this->handleArray($value);
        } else {
            if (in_array($key, $filter)) {
                // put this full path in the new array
            }
        }
    }
}

[已解决]更新:

感谢 @trincot

我解决了我的问题

我使用了他的代码并添加了一个额外的检查以将具有多个值的数组添加到新数组

我解决问题的代码:

<?php
function isListOfValues($array) {            
    $listOfArrays = [];

    foreach ($array as $key => $value) {
        $listOfArrays[] = ! is_array($value) && is_int($key);
    }

    return array_sum($listOfArrays) === count($listOfArrays);
}

function filterKeysRecursive(&$arr, &$keep) {
    $result = [];

    foreach ($arr as $key => $value) {
        if (is_array($value) && ! isListOfValues($value)) {
            $value = filterKeysRecursive($value, $keep);
            
            if (count($value)) {
                $result[$key] = $value;
            }                    
        } else if (array_key_exists($key, $keep)) {
            $result[$key] = $value;
        }
    }
    
    return $result;
}

$keep = array_flip(["title", "width", "height", "gold_members"]);
        
$result = filterKeysRecursive($originalArray, $keep);

我对你的建议是编写一个自定义函数来将结构从一种模式转换为另一种模式:

function transform(array $originalArray): array {
    array_walk($originalArray['images'], function (&$a, $k) {
      unset($a['id']); unset($a['src']);
    });
    unset($originalArray['description']);
    unset($originalArray['price']['normal']);
    unset($originalArray['price']['discount']['silver_members']);
    unset($originalArray['price']['discount']['bronze_members']);
    
    return $originalArray;
}
var_dump(transform($originalArray));

如果您熟悉 OOP,我建议您查看如何 DTO works in API Platform for example 并通过创建自定义 DataTransformer 将此想法注入您的代码,您可以在其中指定要使用转换器和方法支持哪种结构将一种结构转换为另一种结构的位置。

您可以使用递归函数,逻辑如下:

  • 基本情况:与键关联的值不是数组(它是“叶子”)。在这种情况下,只有当键位于所需键列表中时,新对象才会具有 key/value。

  • 递归情况:与键关联的值是一个数组。对该值应用递归。只有当返回结果不是空数组时才添加key。在这种情况下,将过滤后的值关联到结果对象中的键。

为了加快查找键列表的速度,最好将该列表翻转为关联数组。

实现如下:

function filter_keys_recursive(&$arr, &$keep) {
    foreach ($arr as $key => $value) {
        if (is_array($value)) {
            $value = filter_keys_recursive($value, $keep);
            if (count($value)) $result[$key] = $value;
        } else if (array_key_exists($key, $keep)) {
            $result[$key] = $value;
        }
    }
    return $result;
}

$originalArray = ["title" => "BACKPACK MULTICOLOUR","description" => "description here","images" => [["id" => 12323123123,"width" => 635,"height" => 560,"src" => "https://example.com"],["id" => 4365656656565,"width" => 635,"height" => 560,"src" => "https://example.com"]],"price" => ["normal" => 11.00,"discount" => ["gold_members" => 9.00,"silver_members" => 10.00,"bronze_members" => null]]];

$keep = array_flip(["title", "width", "height", "gold_members"]);

$result = filter_keys_recursive($originalArray, $keep);
  • 在每个键和子数组上递归迭代数组。
  • 如果 foreach 中的当前键是结果中的必需键,则:
    • 如果值不是数组,直接赋值
    • 如果该值是一个数组,则以递归方式进一步向下迭代该值,以防需要对子数组键进行任何其他过滤。
  • 如果 foreach 中的当前键 不是 结果中的必需键,则:
    • 如果值本身是一个数组,则递归迭代它。这是必需的,因为可能有我们需要的深层过滤键之一。获取结果,如果结果不是空数组,则仅将其包含在当前子结果中。否则,我们可以安全地跳过它,因为该行下没有必需的键。

片段:

<?php

function filterKeys($array, $filter_keys) {
    $sub_result = [];
    foreach ($array as $key => $value) {
        if(in_array($key, $filter_keys)){// if $key itself is present in $filter_keys
            if(!is_array($value)) $sub_result[$key] = $value;       
            else{
                $temp = filterKeys($value, $filter_keys);
                $sub_result[$key] = count($temp) > 0 ? $temp : $value;
            }
        }else if(is_array($value)){// if $key is not present in $filter_keys - iterate over the remaining subarray for that key
            $temp = filterKeys($value, $filter_keys);
            if(count($temp) > 0) $sub_result[$key] = $temp;
        }
    }
    
    return $sub_result;
}

$result = filterKeys($originalArray, ["title", "width", "height", "gold_members"]);

print_r($result);

Online Demo

试试这个方法。

    $expectedKeys = ['title','images','width','height','price','gold_members'];

    function removeUnexpectedKeys ($originalArray,$expectedKeys)
    {
        foreach ($originalArray as $key=>$value) {
          if(is_array($value)) {
            $originalArray[$key] = removeUnexpectedKeys($value,$expectedKeys);
            if(!is_array($originalArray[$key]) or count($originalArray[$key]) == 0) {
                unset($originalArray[$key]);
            }
          } else {
              if (!in_array($key,$expectedKeys)){
                  unset($originalArray[$key]);
              }
          }
        }
        return $originalArray;
    }
    
    $newArray = removeUnexpectedKeys ($originalArray,$expectedKeys);
    print_r($newArray);

在编辑器上检查这个, https://www.online-ide.com/vFN69waXMf