phparraysrecursionmultidimensional-arrayarray-key

Recursively extract key paths from array - excluding numeric keys


In php, I need a function to return all keys in array of objects. For arrays and objects I need keys like parentkey->key. If the key is again an array, then I need parentkey->key->key1.

My code as below

$array=json_decode('{"client":"4","gateWay":"1","store":"store.shop.com",
"valid":"true","po":34535,"additionalPO":23423,"customerNotes":"",
"orderItems":[{"item":"123","quantity":10,"supplierLotNo":"",
"customsValue":"","customsDescription":"","hsCode":""},
{"item":"345","quantity":50}],
"shippingInfos":[{"address":{"city":"Chennai",
"country":"India","postalCode":"86715","state":"TN",
"streetAddress1":"6971 North Street","streetAddress2":null},
"contact":{"company":null,"email":"info@store.com","firstName":"test",
"lastName":"test","phoneNo":null},"ServiceId":"3","thirdPartyAccountNo":"",
"signatureConfirmation":false,"saturdayDelivery":false}]}',true);

function array_keys_multi(array $array,$headkey,$pkey){
    $keys = array();$hkey='';$parentkey='';
    foreach ($array as $key => $value) {
        //echo $key;
        if($headkey!='' && !is_integer($headkey)){
            if(!is_integer($key)){
                if (is_array($value) || is_object($value)) {
                    if($pkey!='')
                       $parentkey=$pkey."->".$key;
                    else 
                        $parentkey=$key;
                        foreach($value as $kk=>$val){
                            foreach($val as $kk1=>$val1){
                                $keys[]=$parentkey."->".$kk1;
                            }
                        }
                        //$keys[]=$parentkey."->".$key;
                }else{
                    $keys[] = $pkey."->".$key;
                    $hkey=$headkey."->".$key;
                }
            }
        }
        else{
            if(!is_integer($key)){
                if($pkey!='')
                    $parentkey=$pkey."->".$key;
                else 
                    $parentkey=$key;
                if (is_array($value) || is_object($value)) {
                    if($pkey!='')
                        $parentkey=$pkey."->".$key;
                    else
                        $parentkey=$key;
                        foreach($value as $kk=>$val){
                            //print_r($val);echo "==========";
                            foreach($val as $kk1=>$val1){
                                $keys[]=$parentkey."->".$kk1;
                            }
                        }
                        $hkey=$key;

                }else{
                    $keys[]=$key;
                }
            }
        }
        
        if (is_array($value) || is_object($value)) {
            echo $key."-----".$hkey."<br>";
            $keys = array_merge($keys, array_keys_multi($value,$hkey,$key));
        }
    }
    return $keys;
}
$k=array_keys_multi($array,'','');
print_r($k);

I need output as an array with the following key paths (notice no numeric keys are retained):

[
    "client",
    "gateWay",
    "store",
    "valid",
    "po",
    "additionalPO",
    "customerNotes",
    "orderItems->item",
    "orderItems->quantity",
    "orderItems->supplierLotNo",
    "orderItems->customsValue",
    "orderItems->customsDescription",
    "orderItems->hsCode",
    "shippingInfos->address->city",
    "shippingInfos->address->country",
    "shippingInfos->address->postalCode",
    "shippingInfos->address->state",
    "shippingInfos->address->streetAddress1",
    "shippingInfos->address->streetAddress2",
    "shippingInfos->contact->company",
    "shippingInfos->contact->email",
    "shippingInfos->contact->firstName",
    "shippingInfos->contact->lastName",
    "shippingInfos->contact->phoneNo",
    "shippingInfos->ServiceId",
    "shippingInfos->thirdPartyAccountNo",
    "shippingInfos->signatureConfirmation",
    "shippingInfos->saturdayDelivery"
]

How can I achieve this?


Solution

  • Starting from this very similar recursive snippet, I changed the key path separator and extended the algorithm to exclude numeric keys and parents with children.

    Code: (Demo)

    function getUniqueObjectKeyPaths($array, $parentKey = "") {
        $keys = [];
        foreach ($array as $parentKey => $v) {
            if (is_array($v)) {
                $nestedKeys = getUniqueObjectKeyPaths($v, $parentKey);
                foreach($nestedKeys as $index => $key) {
                    if (!is_numeric($parentKey) && !is_numeric($key)) {
                        $nestedKeys[$index] = $parentKey . "->" . $key;
                    }
                }
                array_push($keys, ...$nestedKeys);
            } elseif (!is_numeric($parentKey)) {
                $keys[] = $parentKey;
            }
        }
        return $keys;
    }
    var_export(getUniqueObjectKeyPaths($array));
    

    Output:

    array (
      0 => 'client',
      1 => 'gateWay',
      2 => 'store',
      3 => 'valid',
      4 => 'po',
      5 => 'additionalPO',
      6 => 'customerNotes',
      7 => 'orderItems->item',
      8 => 'orderItems->quantity',
      9 => 'orderItems->supplierLotNo',
      10 => 'orderItems->customsValue',
      11 => 'orderItems->customsDescription',
      12 => 'orderItems->hsCode',
      13 => 'orderItems->item',
      14 => 'orderItems->quantity',
      15 => 'shippingInfos->address->city',
      16 => 'shippingInfos->address->country',
      17 => 'shippingInfos->address->postalCode',
      18 => 'shippingInfos->address->state',
      19 => 'shippingInfos->address->streetAddress1',
      20 => 'shippingInfos->address->streetAddress2',
      21 => 'shippingInfos->contact->company',
      22 => 'shippingInfos->contact->email',
      23 => 'shippingInfos->contact->firstName',
      24 => 'shippingInfos->contact->lastName',
      25 => 'shippingInfos->contact->phoneNo',
      26 => 'shippingInfos->ServiceId',
      27 => 'shippingInfos->thirdPartyAccountNo',
      28 => 'shippingInfos->signatureConfirmation',
      29 => 'shippingInfos->saturdayDelivery',
    )