1

I'm working on project and I'm trying to refactor a 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 be adapt. and that's I'm trying to do.

3 Answers 3

2

Thanks for @404-not-found his code was amazing but missing a small thing which is in this code

if (is_array($key)) {
   $val[$objectKey] = filter_multidimensional($array, $key);
}

you still give the find function the base array which will return the first value it will find in the case of ['user'=>['id',"country"=>['id']]] and the solution for this will be passing the parent array of the object key

So the full code will be


<?php

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(findTill($array,$objectKey), $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;
}


function findTill($array,$key) {
    if (array_key_exists($key, $array)) {
        return $array[$key];
    }
    return findTill(nextLevel($array),$key);
}

/**
 * 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',
        []
    );
}
//-------

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

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

Sign up to request clarification or add additional context in comments.

Comments

2

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

3 Comments

thank you very much, this is real great work!!! , just small issue: I believe we should check the parent key name before we set the value >> check this out: pastebin.com/embed_iframe/2iUksnFY
here is the code response with the issue : pastebin.com/embed_iframe/QuZQLCac?theme=dark
Any solution? ,
1

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

2 Comments

thanks for your answer, this is a really good answer,, but as I said in update 1: I don't know the path of indexes in the json, its exist in the json (that's for sure),but i must go through the array until you find the $key
I think data_get can accept placeholders. Maybe something like '*'.$get_position could be useful

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.