loading...
Cover image for Remove Duplicate Value from Nested Associative Array

Remove Duplicate Value from Nested Associative Array

aldora713 profile image ShakalakaB Originally published at shakalakab.github.io ・2 min read

Romoving duplicate value from nested array is kind of tricky, the main problem is how to loop through and remove element while keeping array reindexed. I would compare three solutions and point out the things I didn't notice.

Question

Supposing we have an array as following, and need to remove duplicate element in 'channels'(as the following comments show).

$arr = [
    [
        'name' => 'a',
        'channels' => [
            ['id' => 1, 'value' => 111],
            ['id' => 2, 'value' => 222],
        ]
    ], [
        'name' => 'b',
        'channels' => [
            ['id' => 3, 'value' => 111], // remove
            ['id' => 4, 'value' => 333],
        ]
    ], [
        'name' => 'c',
        'channels' => [
            ['id' => 5, 'value' => 333], // remove
            ['id' => 6, 'value' => 333], // remove
            ['id' => 7, 'value' => 666],
            ['id' => 8, 'value' => 666], // remove
            ['id' => 9, 'value' => 888],
        ]
    ]
];

*Notice: PHP-version 7.3.14

Solution 1: using foreach and unset

$unique = [];
foreach ($arr as &$item) {
    foreach ($item['channels'] as $key => $channelItem) {
        if (in_array($channelItem['value'], $unique)) {
            unset($item['channels'][$key]);
            continue;
        }
        $unique[] = $channelItem['value'];
    }

print_r($arr);

Result

Array
(
    [0] => Array
        (
            [name] => a
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [value] => 111
                        )

                    [1] => Array
                        (
                            [id] => 2
                            [value] => 222
                        )

                )

        )

    [1] => Array
        (
            [name] => b
            [channels] => Array
                (
                    [1] => Array
                        (
                            [id] => 4
                            [value] => 333
                        )

                )

        )

    [2] => Array
        (
            [name] => c
            [channels] => Array
                (google font
                    [2] => Array
                        (
                            [id] => 7
                            [value] => 666
                        )

                    [4] => Array
                        (
                            [id] => 9
                            [value] => 888
                        )

                )

        )

)

This solution looks fine, but it would cause problem when sent as json response. Noticing the key in $arr[1]['channels] and $arr[2]['channels], they are not in sequential, this could make the array after sent as json difficult to loop. (This could be solved via array_values())

Solution 2: using foreach and array_splice

array_splice would reindex array automatically after remove element.

$unique = [];
foreach ($arr as &$item) {
    foreach ($item['channels'] as $key => $channelItem) {
        if (in_array($channelItem['value'], $unique)) {
            array_splice($item['channels'], $key, 1);
            continue;
        }
        $unique[] = $channelItem['value'];
    }

print_r($arr);

Result

Array
(
    [0] => Array
        (
            [name] => a
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [value] => 111
                        )

                    [1] => Array
                        (
                            [id] => 2
                            [value] => 222
                        )

                )

        )

    [1] => Array
        (
            [name] => b
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 4
                            [value] => 333
                        )

                )

        )

    [2] => Array
        (
            [name] => c
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 6
                            [value] => 333
                        )

                    [1] => Array
                        (
                            [id] => 8
                            [value] => 666
                        )

                    [2] => Array
                        (
                            [id] => 9
                            [value] => 888
                        )

                )

        )

)

This solution hasn't filtered out duplicate value in$arr[2]['channels'].
It's because the internal array pointer in foreach is not synchronous with array_splice. Not matter how
array_splice reindex array, foreach would just keep the index as beginning, so same index in these two ways would point to different value once reindex happens.

Solution 3: using array_walk and array_splice

$unique = [];
foreach ($arr as &$item) {
    array_walk($item['channels'], function ($channelItem, $key) use (&$unique, &$item) {
        if (in_array($channelItem['value'], $unique)) {
            array_splice($item['channels'], $key, 1);
        }
        $unique[] = $channelItem['value'];
    });
}
print_r($arr);

Result

Array
(
    [0] => Array
        (
            [name] => a
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [value] => 111
                        )

                    [1] => Array
                        (
                            [id] => 2
                            [value] => 222
                        )

                )

        )

    [1] => Array
        (
            [name] => b
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 4
                            [value] => 333
                        )

                )

        )

    [2] => Array
        (
            [name] => c
            [channels] => Array
                (
                    [0] => Array
                        (
                            [id] => 7
                            [value] => 666
                        )

                    [1] => Array
                        (
                            [id] => 9
                            [value] => 888
                        )

                )

        )

)

Now, we finally get the result we want. Notice: add & in anoymous function use.
Notice: In PHP 7.2, we can't get the same result, cause array_walk and array_splice index are not synchronous.

Posted on by:

Discussion

markdown guide