0

I've searched stackoverflow and found many similar questions which where solved with array_slice, array_splice and the occasional array_merge, but none of these worked for me and I'm stumped. I must be doing something wrong, but can't figure out what exactly.

My problem: I have a nested associative array representing a menu structure. It looks like this

$this->navigation_array=[
  "CATEGORY_MAIN"=>[
    "Page1"=>"page1.php",
    "Page2"=>"page2.php",
    "Page3"=>"page3.php",
    "Page4"=>"page4.php"
  ],
  "CATEGORY_NEW"=>[
    "Page1"=>"page1_edit.php",
    "Page2"=>"page2_edit.php",
    "Page3"=>"page3_edit.php"
  ],
  "CATEGORY_EMPTY1"=>[],
  "CATEGORY_EMPTY2"=>[],
  "SEARCH"=>[
    "Page1"=>"page1_search.php",
    "Page2"=>"page2_search.php",
    "Page3"=>"page3_search.php"
  ],
  "BACK"=>["Home"=>"index.php"]
];

Now I need a function to add a new category at a given position from either the beginning or the end of the navigation_array. So, either $MENU->category_add("test",2) or $MENU->category_add("test",-1)

What I tried so far fails, even though I tried several different approaches from here, here or here. The current, non-functional, iteration looks like this

public function category_add(string $category,int $position=0): bool{
  if(array_key_exists($category,$this->navigation_array))return false;
  if($position!=0){
    $category_array=array($category=>[]);
    array_splice($this->navigation_array,$position,0,$category_array);
  }else $this->navigation_array[$category]=array(); //simply append if pos is 0
  return true;
}

It does insert at the correct position when I call it with $MENU->category_add("test",2) or $MENU->category_add("test",-1) but what is inserted is [0] => Array ( ) instead of ["test"] => Array ( ). The resulting navigation array thus looks like this:

Array ( [CATEGORY_MAIN] => Array ( [Page1] => page1.php [Page2] => page2.php [Page3] => page3.php [Page4] => page4.php ) [CATEGORY_NEW] => Array ( [Page1] => page1_edit.php [Page2] => page2_edit.php [Page3] => page3_edit.php ) [CATEGORY_EMPTY1] => Array ( ) [CATEGORY_EMPTY2] => Array ( ) [SEARCH] => Array ( [Page1] => page1_search.php [Page2] => page2_search.php [Page3] => page3_search.php ) [0] => Array ( ) [BACK] => Array ( [Home] => index.php ) ) 

But should look like

Array ( [CATEGORY_MAIN] => Array ( [Page1] => page1.php [Page2] => page2.php [Page3] => page3.php [Page4] => page4.php ) [CATEGORY_NEW] => Array ( [Page1] => page1_edit.php [Page2] => page2_edit.php [Page3] => page3_edit.php ) [CATEGORY_EMPTY1] => Array ( ) [CATEGORY_EMPTY2] => Array ( ) [SEARCH] => Array ( [Page1] => page1_search.php [Page2] => page2_search.php [Page3] => page3_search.php ) [test] => Array ( ) [BACK] => Array ( [Home] => index.php ) ) 

I know it's probably something pretty minor or silly that I'm overlooking, but I've stared at this so long, I'm obviously blind to it. Could somebody be so kind and give me a hint?

Edit So according to the php doc, ""Note: Keys in the replacement array are not preserved." array_splice does not work. But there must be another way to somehow achieve this, right?

viamonster
  • 27
  • 6
  • The expected outcome is to have `["test"] => Array ( )` at the second or second last position in the navigation_array. But instead I get `[0] => Array ( )` – viamonster Jul 19 '22 at 07:09
  • 1
    https://www.php.net/manual/en/function.array-splice.php: _"Note: Keys in the replacement array are not preserved."_ – CBroe Jul 19 '22 at 07:10
  • Dang, I knew it was something like that. But how does one work around that? I tried a combination of array_slice and array_merge, but to no avail. – viamonster Jul 19 '22 at 07:13
  • @viamonster Please share a proper indented output. You can't throw some text and expect us to analyze it. Also, you still haven't made clear whether this output is for `$MENU->category_add("test",2)` or `$MENU->category_add("test",-1)` ? – nice_dev Jul 19 '22 at 07:45

2 Answers2

1

Try this code, it has two functions category_add which is calling arrayInsert custom function, we can add a whole new category as well as insert new page inside of existing category.

<?php
$navigation_array = [
    "CATEGORY_MAIN" => [
        "Page1" => "page1.php",
        "Page2" => "page2.php",
        "Page3" => "page3.php",
        "Page4" => "page4.php"
    ],
    "CATEGORY_NEW" => [
        "Page1" => "page1_edit.php",
        "Page2" => "page2_edit.php",
        "Page3" => "page3_edit.php"
    ],
    "CATEGORY_EMPTY1" => [],
    "CATEGORY_EMPTY2" => [],
    "SEARCH" => [
        "Page1" => "page1_search.php",
        "Page2" => "page2_search.php",
        "Page3" => "page3_search.php"
    ],
    "BACK" => [
        "Home" => "index.php"
    ]
];
function category_add(array $array, string $category, array $newElement, int $position = 0)
{
    if (array_key_exists($category, $array)) {
        $categoryArray = $array[$category];
        $categoryArray = arrayInsert($categoryArray, $position, $newElement);
        $array[$category] = $categoryArray;
    } else {
        $array[$category] = $newElement;
    }
    return $array;
}
function arrayInsert($array, $position, $insertArray)
{
    $ret = [];

    if ($position == count($array)) {
        $ret = $array + $insertArray;
    } else {
        $i = 0;
        foreach ($array as $key => $value) {
            if ($position == $i++) {
                $ret += $insertArray;
            }

            $ret[$key] = $value;
        }
    }

    return $ret;
}
$newNavigation = category_add($navigation_array, 'CATEGORY_MAIN', ['Page2aaa' => "page2aaa.php"], 1);
echo '<pre>'; print_r($newNavigation);
// create whole new category
$newNavigation2 = category_add($navigation_array, 'CATEGORY_NEW2', ['new' => "new.php"]);
echo '<pre>'; print_r($newNavigation2);
Bhaumik Pandhi
  • 2,655
  • 2
  • 21
  • 38
  • Thank you! I adapted your answer a bit to better integrate it in my code (as I e.g. already have a distinct function to add pages to a category), but it definitely set me on the right path. – viamonster Jul 19 '22 at 08:11
1

https://www.php.net/manual/en/function.array-splice.php#111204

When trying to splice an associative array into another, array_splice is missing two key ingredients:

  • a string key for identifying the offset
  • the ability to preserve keys in the replacement array

This is primarily useful when you want to replace an item in an array with another item, but want to maintain the ordering of the array without rebuilding the array one entry at a time.

<?php
function array_splice_assoc(&$input, $offset, $length, $replacement) {
        $replacement = (array) $replacement;
        $key_indices = array_flip(array_keys($input));
        if (isset($input[$offset]) && is_string($offset)) {
                $offset = $key_indices[$offset];
        }
        if (isset($input[$length]) && is_string($length)) {
                $length = $key_indices[$length] - $offset;
        }

        $input = array_slice($input, 0, $offset, TRUE)
                + $replacement
                + array_slice($input, $offset + $length, NULL, TRUE);
}

$fruit = array(
        'orange' => 'orange',
        'lemon' => 'yellow',
        'lime' => 'green',
        'grape' => 'purple',
        'cherry' => 'red',
);

// Replace lemon and lime with apple
array_splice_assoc($fruit, 'lemon', 'grape', array('apple' => 'red'));

// Replace cherry with strawberry
array_splice_assoc($fruit, 'cherry', 1, array('strawberry' => 'red'));
?>

Wei Wang
  • 69
  • 1
  • Thanks, this is an interesting function, but as far as I can see, it does replace stuff not insert it inbetween. – viamonster Jul 19 '22 at 08:09