Search code examples
phparraysfunctiontreestructure

PHP - Remove empty menus


Good afternoon guys, I'm having a hard time finding the solution to this simple problem, I hope someone can help me.

I have a recursive array automatically generated by the source code, I use this array as the system's menu tree, however, it goes through a permission system, and some submenus are excluded, leaving certain menus empty, follow the example in JSON:

{
    "1": {
        "id": 1,
        "idFather": null,
        "nome": "test 1",
        "sub": {
            "4": {
                "id": 4,
                "idFather": 1,
                "nome": "test 1.1",
                "sub": {}
            },
            "5": {
                "id": 5,
                "idFather": 1,
                "nome": "test 1.2",
                "sub": {
                    "7": {
                        "id": 7,
                        "idFather": 5,
                        "nome": "test 1.3.1",
                        "sub": {}
                    },
                    "8": {
                        "id": 8,
                        "idFather": 5,
                        "nome": "test 1.3.2",
                        "sub": {}
                    }
                }
            },
            "6": {
                "id": 6,
                "idFather": 1,
                "nome": "test 1.3"
            }
        }
    },
    "2": {
        "id": 2,
        "idFather": null,
        "nome": "test 2"
    },
    "3": {
        "id": 3,
        "idFather": null,
        "nome": "test 3",
        "sub": {
            "10": {
                "id": 10,
                "idFather": 3,
                "nome": "test 3.2"
            }
        }
    }
}

Within key 1 I have key 4 with no other items, and I have key 5 with two keys (7 and 8), however, these 2 keys also do not contain items, so I need to remove keys 4, 7, 8 and consequently, key 5 too, as it will be empty at the end of the removals! Note that key 6 inside key 1, key 10 inside key 3 and key 2 do not contain the "sub" element, so it must not be removed!

I am Brazilian, so my English may be a little rusty.


Solution

  • A simple recursive function will handle this.

    • Check each entry at the current level.
    • If it has a sub menu, call the function on the sub menu. If not, move on.
    • If the sub menu is now empty, remove it

    The implementation looks like this:

    $jsonString = "{...}"; // Your data listed above, omitted here for clarity
    
    $menus = json_decode($jsonString);
    
    // Pass a menu into the function. 
    // PHP passes objects by reference, so we're operating directly
    // on the original object, hence no need for a return value.
    
    function clearMenu(object $menu):void {
    
        foreach($menu as $id=>$entry) {
            if (isset($entry->sub)) {              // If we have a submenu, handle it
                clearMenu($entry->sub);
                if (empty((array)$entry->sub)) {  // Cast object to array to test for emptiness
                    unset($menu->$id);            // Unset the item in the menu using the key. Unsetting the item directly doesn't work
                }
            }
        }
    }
    
    clearMenu($menus);
    
    $newMenus = json_encode($menus, JSON_PRETTY_PRINT);
    echo "<pre>$newMenus</pre>";
    

    Output:

    {
        "1": {
            "id": 1,
            "idFather": null,
            "nome": "test 1",
            "sub": {
                "6": {
                    "id": 6,
                    "idFather": 1,
                    "nome": "test 1.3"
                }
            }
        },
        "2": {
            "id": 2,
            "idFather": null,
            "nome": "test 2"
        },
        "3": {
            "id": 3,
            "idFather": null,
            "nome": "test 3",
            "sub": {
                "10": {
                    "id": 10,
                    "idFather": 3,
                    "nome": "test 3.2"
                }
            }
        }
    }