Bene
Bene

Reputation: 1281

Sort array of categories by root category first

I would like sort a given array of categories. The root categories must appear first before their child categories in a new array.

Example data:

$data = [
  ['id' => '1', 'name' => 'abc', 'parent_id' => '0' ],
  ['id' => '4', 'name' => 'def', 'parent_id' => '5' ],
  ['id' => '3', 'name' => 'ghj', 'parent_id' => '2' ],
  ['id' => '5', 'name' => 'zst', 'parent_id' => '1' ],
  ['id' => '2', 'name' => 'klm', 'parent_id' => '1' ],
];

Output I would like to achieve:

$output = [
  '1' => ['id' => '1', 'name' => 'abc', 'parent_id' => '0' ],
  '5' => ['id' => '5', 'name' => 'zst', 'parent_id' => '1' ],
  '4' => ['id' => '4', 'name' => 'def', 'parent_id' => '5' ],
  '2' => ['id' => '2', 'name' => 'klm', 'parent_id' => '1' ],
  '3' => ['id' => '3', 'name' => 'ghj', 'parent_id' => '2' ],
];

My code until now:

function sortCategories($data) {
  $output = [];
  foreach ($data as $category) {
    if ($output[$category['id']] || $output[$category['parent_id']])
      continue;

    if (!$output[$category['parent_id']])
      $output = getParentCategories($category, $ouput, $data);

    $output[$category['id']] = [
      'id' => $category['id'],
      'name' => $category['name'],
      'parent_id' => $category['parent_id']
    ];
  }
  return $output;
}

function getParentCategories($category, $output, $data) {
  if ($output[$category['parent_id']]) {
    return $output;
  } else {
  }
}

The problem I have is that I don't know how to probably implement getParentCategories() so that this function handles root categories the right way.

Any suggestions how to solve the problem?

Upvotes: 1

Views: 143

Answers (2)

Death-is-the-real-truth
Death-is-the-real-truth

Reputation: 72299

A solution using foreach() will work for you:-

<?php 
$data = [
  ['id' => '1', 'name' => 'abc', 'parent_id' => '0' ],
  ['id' => '4', 'name' => 'def', 'parent_id' => '5' ],
  ['id' => '3', 'name' => 'ghj', 'parent_id' => '2' ],
  ['id' => '5', 'name' => 'zst', 'parent_id' => '1' ],
  ['id' => '2', 'name' => 'klm', 'parent_id' => '1' ],
]; // original array

$new_array = array(); // new empty array

foreach($data as $key=>$value){ // iterate through original array
    if($value['parent_id'] == 0){ // check root value
        $new_array[$value['id']] = $value; // assign full sub-array to newly created array
    }else{ // if not root value
        $get_key = searchForId($value['parent_id'],$data); // based on parent-id get the parent array key from original array
            $new_array[$data[$get_key]['id']] = $data[$get_key]; // based on key assign parent array to the newly created array
            $new_array[$value['id']] = $value; // assign the child array to newly created array
    }

}
function searchForId($id, $array) { // send parent_id and original array
   foreach ($array as $key => $val) {
       if ($val['id'] === $id) {
           return $key; // return key of parent id
       }
   }
   return null;
}
echo "<pre/>";print_r($new_array); // print new array
?>

Output:- https://eval.in/599837

Upvotes: 1

PaulH
PaulH

Reputation: 3049

Since php 7 we can use the spaceship operator like this

usort($output, function ($a,$b) {
   return $a['parent_id'].$a['id'] <=> $b['parent_id'].$b['id'];
});

Both id's are concatenated so that id and parent_id are sorted and parent_id has priority over id.

In a real case, the id's may have a range > 0..9 and you want 5 < 12, which is not the case when comparing strings as in the code above. Maybe you can post a code that takes that into account and have us review it. Maybe also learn about the difference between '1' => ['id' => '1'] and 1 => ['id' => 1] first.

Upvotes: 1

Related Questions