简体   繁体   English

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

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

I have a multidimensional array that can have any depth.我有一个可以有任何深度的多维数组。 What im trying to do is to filter the whole path based on dynamic keys and create a new array of it.我想做的是根据动态键过滤整个路径并创建一个新数组。

Example of the array数组示例

$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
        ]
    ]
];

Example how the output should look like with the key "title, width, height, gold_members" filtered out.示例输出应该如何与键“标题,宽度,高度,gold_members”过滤掉。 Only keys from the end of the array tree should be valid, so nothing must happen when images is in the filter只有来自数组树末尾的键应该是有效的,所以当图像在过滤器中时不会发生任何事情

$newArray = [
    "title" => "BACKPACK MULTICOLOUR",
    "images" => [
        [
            "width" => 635,
            "height" => 560,
        ],
        [
            "width" => 635,
            "height" => 560,
        ]
    ],
    "price" => [
        "discount" => [
            "gold_members" => 9.00,
        ]
    ]
];

I guess that i should create a function that loop through each element and when it is an associative array, it should call itself again我想我应该创建一个循环遍历每个元素的函数,当它是一个关联数组时,它应该再次调用自己

Because the filtered paths are unknown i cannot make a hardcoded setter like this:因为过滤后的路径是未知的,所以我无法制作这样的硬编码设置器:

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

The following filter will be an example but it should basically be dynamic以下过滤器将是一个示例,但它基本上应该是动态的

example what i have now:例如我现在拥有的:

$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
            }
        }
    }
}

[Solved] Update: [已解决] 更新:

I solved my problem thanks to @trincot感谢@trincot ,我解决了我的问题

I used his code and added an extra check to add an array with multiple values to the new array我使用了他的代码并添加了一个额外的检查以将具有多个值的数组添加到新数组中

My code to solve the issue:我解决问题的代码:

<?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);

My proposition to you is to write a custom function to transform structure from one schema to another:我对您的建议是编写一个自定义函数来将结构从一种模式转换为另一种模式:

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));

If you are familiar with OOP I suggest you to look at how DTO works in API Platform for example and inject this idea into your code by creating custom DataTransformers where you specify which kind of structers you want to support with transformer and a method where you transform one structure to another.如果您熟悉 OOP,我建议您例如查看DTO在 API 平台中的工作原理,并通过创建自定义 DataTransformers 将这个想法注入您的代码,您可以在其中指定要使用转换器支持的结构类型和转换方法一个结构到另一个结构。

You could use a recursive function, with following logic:您可以使用具有以下逻辑的递归函数:

  • base case: the value associated with a key is not an array (it is a "leaf").基本情况:与键关联的值不是数组(它是“叶子”)。 In that case the new object will have that key/value only when the key is in the list of desired keys.在这种情况下,仅当键位于所需键列表中时,新对象才会具有该键/值。

  • recursive case: the value associated with a key is an array.递归情况:与键关联的值是一个数组。 Apply recursion to that value.对该值应用递归。 Only add the key when the returned result is not an empty array.仅当返回的结果不是空数组时才添加键。 In that case associate the filtered value to the key in the result object.在这种情况下,将过滤后的值与结果对象中的键相关联。

To speed up the look up in the list of keys, it is better to flip that list into an associative array.为了加快在键列表中的查找速度,最好将该列表翻转为关联数组。

Here is the implementation:这是实现:

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);
  • Iterate over the array recursively on each key and subarray.在每个键和子数组上递归迭代数组。
  • If the current key in the foreach is a required key in the result then:如果 foreach 中的当前键是结果中的必需键,则:
    • If the value is not an array, simply assign the value如果值不是数组,只需赋值
    • If the value is an array, iterate further down over value recursively just in case if there is any other filtering of the subarray keys that needs to be done.如果该值是一个数组,则递归地向下迭代该值,以防万一需要对子数组键进行任何其他过滤。
  • If the current key in the foreach is NOT a required key in the result then:如果 foreach 中的当前键不是结果中的必需键,则:
    • Iterate over value recursively if it's an array in itself.如果它本身是一个数组,则递归迭代值。 This is required because there could be one of the filter keys deep down which we would need.这是必需的,因为可能存在我们需要的深层过滤器键之一。 Get the result and only include it in the current subresult if it's result is not an empty array.如果结果不是空数组,则获取结果并仅将其包含在当前子结果中。 Else, we can skip it safely as there are no required keys down that line.否则,我们可以安全地跳过它,因为该行没有必需的键。

Snippet:片段:

<?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在线演示

Try this way.试试这个方法。

    $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);

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM