PHP中的array_unique函数

首先

你好。

我是y-encore,是第一年的服务器端工程师,在格伦吉2017年Advent日历上发表的12月8日的文章。今天我想谈谈我们在开发中使用的PHP的array_unique函数。(很抱歉,我将正题的内容改变了)

array_unique是什么?

有关内容请点击这里。
http://php.net/manual/ja/function.array-unique.php

使用PHP的用户可能会知道,这是一个用于删除数组中重复值的函数。

当我们现实中举个例子时,

$testArray = [
    'one' => 1,
    'two' => 1,
    'three' => 2,
    'four' => 1,
    'five' => 3,
];

print_r(array_unique($testArray));
Array
(
    [one] => 1
    [three] => 2
    [five] => 3
)

没有关系没有key。即使数字和字符串混合,也可以删除转换为字符串后相等的项。

$testArray = [
    1,
    '1',
    2,
    1,
    '3',
];

print_r(array_unique($testArray));
Array
(
    [0] => 1
    [2] => 2
    [4] => 3
)

想在多维数组中应用。

有时候我们想要以关联数组的形式获取数据,并创建一个整理后的数组,然后从中删除重复的数据。

在之前的php.net说明中,

注意:请注意,array_unique() 不适用于多维数组。

然而,当你搜索”array_unique 多维数组”时,你会发现如果在array_unique的第二个参数sort_flags中指定SORT_REGULAR,似乎会得到良好的结果。

$testArray = [
    'one' => [
        'first' => 1,
        'second' => 2,
    ],
    'two' => [
        'first' => 1,
        'second' => 2,
    ],
    'three' => [
        'first' => 0,
        'second' => 2,
    ],
    'four' => [
        'first' => 1,
        'second' => 2,
    ],
];

print_r(array_unique($testArray, SORT_REGULAR));
Array
(
    [one] => Array
        (
            [first] => 1
            [second] => 2
        )
    [three] => Array
        (
            [first] => 0
            [second] => 2
        )
)

我明白了,看起来重复数据被巧妙地删除了。

有时候会出现不顺利的情况

php_unique的实现首先对数组进行一次排序,然后去除重复的元素以进行重复判断。
之前提到的sort_flags是指定排序时要匹配的数据类型,如果是SORT_REGULAR,则进行比较和排序时不进行特殊的类型转换。
由于需要进行排序才能判断重复,如果排序时比较不成功的元素包含在内,则array_unique的结果将不稳定。

我們使用SORT_REGULAR來對多維數組進行array_unique操作,但正如這篇文章所指出的,當數字和字符串混合時,可能會表現出不穩定的行為。

在某些情况下,当想要对具有不同结构的关联数组进行重复检查时,由于无法很好地比较元素,array_unique的结果将变得不稳定。让我们举一个具体的例子来说明。

首先,我们将创建一个方法,用于创建无法进行简单比较的关联数组。

function createTestArray($keyName) {
    return [
        'one' => 1,
        'two' => 2,
        $keyName => 3,
    ];
}

echo '(three < san) ' . (createTestArray('three') < createTestArray('san') ? 'true' : 'false');
echo '(three > san) ' . (createTestArray('three') > createTestArray('san') ? 'true' : 'false');
echo '(three === san) ' . (createTestArray('three') === createTestArray('san') ? 'true' : 'false');

echo '(three === three) ' . (createTestArray('three') === createTestArray('three') ? 'true' : 'false');
echo '(san === san) ' . (createTestArray('san') === createTestArray('san') ? 'true' : 'false');
(three < san) : false
(three > san) : false
(three === san) : false

(three === three) : true
(san === san) : true

在具有不同结构的关联数组之间进行大小比较都会得到false,简单的比较不能进行排序。我们尝试对包含这种关联数组作为列表的数组使用array_unique。

$keyNameList = [
    'three',
    'san',
];

$testArrayList = [];
for ($i = 0; $i < 30; $i++) {
    $testArrayList[] = createTestArray($keyNameList[rand(0, 1)]);
}

print_r(array_unique($testArrayList, SORT_REGULAR));
Array
(
    [0] => Array
        (
            [one] => 1
            [two] => 2
            [san] => 3
        )
    [1] => Array
        (
            [one] => 1
            [two] => 2
            [san] => 3
        )
    [3] => Array
        (
            [one] => 1
            [two] => 2
            [three] => 3
        )
    [18] => Array
        (
            [one] => 1
            [two] => 2
            [three] => 3
        )
)

有一些确实消失了,但仍然可以确认重复的元素仍然存在。

不使用array_unique来删除重复元素

在去除具有不同结构的关联数组元素的重复时,我认为不使用array_unique并自己实现是安全的。虽然计算成本会增加,但你可以使用foreach和in_array相对容易地编写它。

function myArrayUnique($array) {
    $uniqueArray = [];
    foreach($array as $key => $value) {
        if (!in_array($value, $uniqueArray)) {
            $uniqueArray[$key] = $value;
        }
    }
    return $uniqueArray;
}

此外,如果不需要键,也可以使用array_reduce和in_array进行编写。由于在去除重复项的情况下通常不需要键值,因此这种方式可能更加优雅。

$uniqueArray = array_reduce($array, function($carry, $item) {
    if (!in_array($item, $carry)) {
        $carry[] = $item;
    }
    return $carry;
}, []);

最后

非常感谢阅读至此!格伦吉的圣诞倒数日还将继续,敬请期待并多多支持。

[前日]12/7日 raitome: 尝试使用最新版的Spine
https://qiita.com/raitome/items/5ae1fce0bb4fc68fc796

Unity的负荷削减,从12月9日开始!