Skip to content
Advertisement

PHP assign parent ids for children in array

I’am stuck on assigning pid’s (parent id’s) on 3rd element. I mean in setParent() if I return $array before call recursive function, then I see that “PC” and “Laptop” are having correct pid’s. If I run all script, then I get $newArr is null. PHP 7.3, Laravel 6.

I want to make this:

[
['pc',         ['pc',null,     null]       ,'id'=>1,'pid'=>1],
['laptop',     ['pc','laptop', null]       ,'id'=>2,'pid'=>1],
['acc',        ['pc','acc',    null]       ,'id'=>3,'pid'=>1],
['bags',       ['pc','acc',   'bags']      ,'id'=>4,'pid'=>3],
['adapter',    ['pc','acc',   'adapter']   ,'id'=>5,'pid'=>3],
['clothes',    ['clothes',null,null]       ,'id'=>6,'pid'=>6]
];

From that :

function test3(){
    $array = [
        ['pc',         ['pc',null,     null]       ,'id'=>1,'pid'=>0],
        ['laptop',     ['pc','laptop', null]       ,'id'=>2,'pid'=>0],
        ['acc',        ['pc','acc',    null]       ,'id'=>3,'pid'=>0],
        ['bags',       ['pc','acc',   'bags']      ,'id'=>4,'pid'=>0],
        ['adapter',    ['pc','acc',   'adapter']   ,'id'=>5,'pid'=>0],
        ['clothes',    ['clothes',null,null]       ,'id'=>6,'pid'=>0]
    ];
    $newArr = $this->setParent($array);
    return  response()->json([$newArr]);
}

function setParent($array, $goStop='go'){
    if($goStop == 'go'){
        $foundPids = 0;
        foreach($array as $k => $v){
            if( $v["pid"] == 0) $foundPids++;
        }
        if( $foundPids == 0){
            return $this->setParent($array, 'stop'); // or return $array;
        }

        // parent search
        foreach($array as $k1 => $v1){
            if ($v1['pid']!=0)
                break;

            $keyInd = array_search($v1[0] , $v1[1]);// $v1 looks like {"0":"pc","1":["pc",null,null],"id":1,"pid":0}

            if($keyInd == 0){
                $array[$k1]['pid'] = $v1['id']; // PC updated
            }

            if($keyInd > 0){
                $parentName = $v1[1][$keyInd-1];
                $IdToWriteInPid = 0;
                foreach ($array as $k1inner => $v1inner){
                    if($v1inner[$keyInd-1] == $parentName){
                        $IdToWriteInPid = $v1inner['id'];
                    }
                }

                $array[$k1]['pid'] = $IdToWriteInPid; // Laptop updated
                //
                // if uncomment, then i see that category PC having pid 1
                // and category Laptop having pid 1. It is ok.
                //
                // return $array;
                //

                return $this->setParent($array, 'go' );
            }
        }
    }
    else return $array;
}

Advertisement

Answer

You could build hierarchy paths for each of your entries using implode, then make use of array_reduce to:

  • index your entries by their path,
  • alter the pid of each subsequent entry if their parent’s path was found.

Finally, array_values will switch back your array’s indexes to numerical format.

Note: this assumes the parents are always defined before their children, like in your sample data.

Note 2: this also assumes the names such as 'pc' cannot contain forward slashes (/). Feel free to change the separator otherwise.

function fillParentIds(array $input): array
{
  return array_values(array_reduce($input, static function (array $entriesByPath, array $entry): array {
    $hierarchy = array_filter($entry[1]);
    $pathToParent = implode('/', array_slice($hierarchy, 0, -1));
    $pathToEntry = implode('/', $hierarchy);

    $entry['pid'] = array_key_exists($pathToParent, $entriesByPath) 
      ? $entriesByPath[$pathToParent]['id'] 
      : $entry['id'];

    $entriesByPath[$pathToEntry] = $entry;
    return $entriesByPath;
  }, []));
}

Usage:

$withParentIds = fillParentIds($input);

Demo: https://3v4l.org/DeORi

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement