Home > Mobile >  refactor multidimensional json in php by keys
refactor multidimensional json in php by keys

Time:11-06

I'm working on project and I'm trying to refactor an json object By array of $keys for example:

  1. as input:
{
    "message": "User Detail",
    "code": 200,
    "error": false,
    "results": {
        "user": {
            "id": 2,
            "name": "ali",
            "country": {
                "id": 50,
                "name": "EGY"
            }
        },
        "access_token": "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o"
    }
}
  1. as input: array of $keys
$kyes = [
    'code',
    'user'=>[
        'id',
        'country'=>['name']
    ]
]

expect to return:

{
    "code": 200,
    "user": {
        "id": 2,
        "country": {
           "name": "egy",
        }
    }
}

code I have tried:

    public function filter_multidimensional(array $array, array $keys){
        $val = [];
        foreach ($array as $key => $value) {
            if (in_array($key,$keys)){
                $val[$key] = $value;
            }elseif (is_array($value)) {
                $val[$key] = $this->filter_multidimensional($keys,$value);
            }
        }
        return $val;
    }
    //-------
    $this->filter_multidimensional($json,['code','user'=>['id','country'=>['name']]])

Unfortunately output: {"code":200,"results":{"user":{"user":{"0":"id","country":{"name":"abdo Elzahaby","country":["name"],"state":[],"city":["name"],"addresses":[]}}}}}

update 1

the json input is not const, so my code must to adapt. and that's I'm trying to do

CodePudding user response:

You could use a combination of data_get and data_set to get what you want.

Change the input a bit so it's more consistent. Dot notation would be easiest.

$array = [
    "message" => "User Detail",
    "code" => 200,
    "error" => false,
    "results" => [
        "user" => [
            "id" => 2,
            "name" => "ali",
            "country" => [
                "id" => 50,
                "name" => "EGY",
            ],
        ],
        "access_token" => "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o",
    ],
];

$keys = [
    'code' => 'code',
    'user.id' => 'results.user.id',
    'user.country.name' => 'results.user.country.name',
];

$results = [];
foreach ($keys as $set_position => $get_position) {
    data_set($results, $set_position, data_get($array, $get_position));
}

CodePudding user response:

I believe this method works. I flipped your logic, and instead of searching the $array to see if it's keys match any of those in $keys, I instead recursively searched $keys to see if $array had any matching values.

function filter_multidimensional(array $array, array $keys) {
    if (empty($keys) || empty($array)) {
        return [];
    }

    $val = [];
    // In the structure of $keys, both key and value are technically "keys". 
    // In the example `['code','user'=>['id']]`, 'code' and 'id' are both array *values*,
    // while 'user' is a key. 
    //
    // So, $objectKey is a search key which contains sub-keys, while $key is just a regular string. 
    foreach ($keys as $objectKey => $key) {
        // If $key is an array, then recursively search, and save the results to the $objectKey string. 
        if (is_array($key)) {
            $val[$objectKey] = filter_multidimensional($array, $key);
        }
        // If the desired key exists, then save the value. 
        else if (array_key_exists($key, $array)) {
            $val[$key] = $array[$key];
        }
        // Otherwise, $key is a string, but it does not exist at this level of $array,
        // so search the next-level up in $array, and merge the results into this level of $val. 
        else {
            $val = array_merge($val, filter_multidimensional(nextLevel($array), [$key]));
        }
    }

    return $val;
}

/**
 * Create an array that contains only the array values from $array.
 * 
 * Eg: If given ['a' => '1', 'b' => ['foo' => '2'], 'c' => ['hello' => 'world']],
 * then return ['foo' => '2', 'hello' => 'world']
 *
 * @param array $array
 * @return array
 */
function nextLevel(array $array) {
    // Merge all of the array values together into one array
    return array_reduce(
        // Strip the keys
        array_values(
            // Filter to return only keys where the value is an array
            array_filter(
                $array, 
                function ($value) {
                    return is_array($value);
                }
            )
        ),
        'array_merge',
        []
    );
}
//-------

$result = filter_multidimensional($obj,['code','user'=>['id','country'=>['name']]]);
  • Related