Mutabor
Mutabor

Reputation: 19

Laravel Collection Split into multi-dimensional array

i need a function to convert a Collection in Laravel to an multi-dimensional array, maybe someone have something like this.

What i have is this collection of permissions:

Illuminate\Support\Collection {#1190 ▼
  #items: array:10 [▼
    0 => "admin.users.view"
    1 => "admin.roles.create"
    2 => "admin.roles.view"
    3 => "admin.roles.edit"
    4 => "admin.roles.delete"
    5 => "admin.roles.set"
    6 => "admin.whitelist.edit"
    7 => "admin.ticket.view"
    8 => "admin.ticket.edit"
    9 => "admin.ticket.create"
  ]
}

Now i want to split it by every dot the result should look like this:

[
    "admin" => [
        "users" => [
            "view"
        ],
        "roles" => [
            "create",
            "view",
            "edit",
            "delete",
            "set"
        ],
        "whitelist" => [
            "edit"
        ],
        "ticket" => [
            "view",
            "edit",
            "create"
        ]
    ]
]

I know it should be possible to work with mapping or create a function for it and make a foreach and stuff like this, but before spending hours to get the solution i would ask if someone have any snipped for it.

Thank you in advance for your support.

Greetings from Germany Patrick

Upvotes: 1

Views: 1771

Answers (2)

IGP
IGP

Reputation: 15786

Using this collection as base:

$collection = collect([
    "admin.users.view",
    "admin.roles.create",
    "admin.roles.view",
    "admin.roles.edit",
    "admin.roles.delete",
    "admin.roles.set",
    "admin.whitelist.edit",
    "admin.ticket.view",
    "admin.ticket.edit",
    "admin.ticket.create"
]);

EDIT After struggling for awhile, I reached a different way to do it adapting the Arr::set function.

$collection->reduce(function ($carry, $item) {
    list($key, $value) = [Str::beforeLast($item, '.'), Str::afterLast($item, '.')];
    $keys = explode('.', $key);

    foreach ($keys as $i => $key) {
        if (count($keys) === 1)
            break;

        unset($keys[$i]);
        if (! isset($array[$key]) || ! is_array($array[$key]))
            $array[$key] =  [];

        $array = &$carry[$key];
    }
    $array[array_shift($keys)][] = $value;

    return $carry;
}, []);

Upvotes: 0

Remy
Remy

Reputation: 807

There might be a more Laravel way of doing this. However, the following code would create a nested array based on the strings you provided.

$result = [];
foreach ($strings as $string) {
    // Assign back to the root (new start for current string)
    $previous = &$result;

    $pieces = explode('.', $string);
    $piecesCount = count($pieces);
    $lastIndex = $piecesCount - 1;

    for ($i = 0; $i < $piecesCount; $i++) {
        // Only interested in pushing the last piece as value.
        if ($i == $lastIndex) {
            $previous[] = $pieces[$i];

            continue;
        }

        if (!key_exists($pieces[$i], $previous)) {
            $previous[$pieces[$i]] = [];
        }

        // Go down the nested array by assigning its reference.
        $previous = &$previous[$pieces[$i]];
    }
}

Result:

Array
(
    [admin] => Array
        (
            [users] => Array
                (
                    [0] => view
                )
            [roles] => Array
                (
                    [0] => create
                    [1] => view
                    [2] => edit
                    [3] => delete
                    [4] => set
                )
            [whitelist] => Array
                (
                    [0] => edit
                )
            [ticket] => Array
                (
                    [0] => view
                    [1] => edit
                    [2] => create
                )
        )
)

Upvotes: 1

Related Questions