1. GROUP BY
一个键
这个函数类似于数组的GROUP BY
,但有一个重要的限制:只能有一个分组“列”($identifier
)。
function arrayUniqueByIdentifier(array $array, string $identifier)
{
$ids = array_column($array, $identifier);
$ids = array_unique($ids);
$array = array_filter($array,
function ($key, $value) use($ids) {
return in_array($value, array_keys($ids));
}, ARRAY_FILTER_USE_BOTH);
return $array;
}
2. 检测表格(二维数组)中的唯一行
这个函数是用来过滤“行”的。如果我们把一个二维数组看作是一张表格,那么它的每一个元素都是一行。因此,我们可以使用此函数来移除重复的行。如果两行(第一维的元素)相等,则表示它们所有列(第二维的元素)都相等。对于“列”值的比较应用以下规则:如果一个值是简单的类型,则会将该值本身用于比较;否则会使用它的类型(array
、object
、resource
、unknown type
)。
策略很简单:从原始数组中创建一个浅层次的数组,其中元素是原始数组的“列”implode
d后的结果;然后在其上应用 array_unique(...)
函数;最后使用检测到的ID来过滤原始数组。
function arrayUniqueByRow(array $table = [], string $implodeSeparator)
{
$elementStrings = [];
foreach ($table as $row) {
$elementPreparedForImplode = array_map(
function ($field) {
$valueType = gettype($field);
$simpleTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
$field = in_array($valueType, $simpleTypes) ? $field : $valueType;
return $field;
}, $row
);
$elementStrings[] = implode($implodeSeparator, $elementPreparedForImplode);
}
$elementStringsUnique = array_unique($elementStrings);
$table = array_intersect_key($table, $elementStringsUnique);
return $table;
}
如果"column"值的类型是object
,也可以改进比较方法,检测其类。
$implodeSeparator
应该更加复杂,例如spl_object_hash($this)
。
3. 检测表格(二维数组)中具有唯一标识列的行
此解决方案依赖于第二个解决方案。现在,完整的“行”不需要是唯一的。如果一个“行”的所有相关“字段”(第二维的元素)与相应“键”(具有相同键的元素)的“字段”(第二维的元素)相等,则两个“行”(第一维的元素)现在相等。
“相关”的“字段”是具有键等于传递的“标识符”元素之一的“字段”(第二维的元素)。
function arrayUniqueByMultipleIdentifiers(array $table, array $identifiers, string $implodeSeparator = null)
{
$arrayForMakingUniqueByRow = $removeArrayColumns($table, $identifiers, true);
$arrayUniqueByRow = $arrayUniqueByRow($arrayForMakingUniqueByRow, $implodeSeparator);
$arrayUniqueByMultipleIdentifiers = array_intersect_key($table, $arrayUniqueByRow);
return $arrayUniqueByMultipleIdentifiers;
}
function removeArrayColumns(array $table, array $columnNames, bool $isWhitelist = false)
{
foreach ($table as $rowKey => $row) {
if (is_array($row)) {
if ($isWhitelist) {
foreach ($row as $fieldName => $fieldValue) {
if (!in_array($fieldName, $columnNames)) {
unset($table[$rowKey][$fieldName]);
}
}
} else {
foreach ($row as $fieldName => $fieldValue) {
if (in_array($fieldName, $columnNames)) {
unset($table[$rowKey][$fieldName]);
}
}
}
}
}
return $table;
}