过滤/删除在多维数组中多次找到列值的行

Iro*_*nic 5 php filtering unique duplicates multidimensional-array

我需要从输入数组中删除特定列中出现重复值的行。

样本数组:

$array = [
    ['user_id' => 82, 'ac_type' => 1],
    ['user_id' => 80, 'ac_type' => 5],
    ['user_id' => 76, 'ac_type' => 1],
    ['user_id' => 82, 'ac_type' => 1],
    ['user_id' => 80, 'ac_type' => 5]
];
Run Code Online (Sandbox Code Playgroud)

我想进行过滤以user_id确保唯一性并实现此结果:

所以,我的输出将是这样的:

[
    ['user_id' => 82, 'ac_type' => 1],
    ['user_id' => 80, 'ac_type' => 5],
    ['user_id' => 76, 'ac_type' => 1]
]
Run Code Online (Sandbox Code Playgroud)

我已经找到了此页面,但没有一个答案适合我的情况:

$result = array_unique($array, SORT_REGULAR);
Run Code Online (Sandbox Code Playgroud)

$result = array_map("unserialize", array_unique(array_map("serialize", $array)));
Run Code Online (Sandbox Code Playgroud)

$result = array();
foreach ($array as $k => $v) {
    $results[implode($v)] = $v;
}
$results = array_values($results);
print_r($results);
Run Code Online (Sandbox Code Playgroud)

但重复的行仍然存在。

mic*_*usa 8

为了获得更清晰的“最小、完整、可验证的示例”,我将在演示中使用以下输入数组:

$array = [
    ['user_id' => 82, 'ac_type' => 1],
    ['user_id' => 80, 'ac_type' => 5],
    ['user_id' => 76, 'ac_type' => 1],
    ['user_id' => 82, 'ac_type' => 2],
    ['user_id' => 80, 'ac_type' => 5]
];
// elements [0] and [3] have the same user_id, but different ac_type
// elements [1] and [4] have identical row data
Run Code Online (Sandbox Code Playgroud)
  1. 无条件地将行推入结果数组并分配关联的第一级键,然后使用 重新索引array_values()。此方法会用较晚出现的重复行覆盖较早出现的重复行。

    数组列演示

    var_export(array_values(array_column($array, null, 'user_id')));
    
    Run Code Online (Sandbox Code Playgroud)

    foreach 演示

    $result = [];
    foreach ($array as $row) {
        $result[$row['user_id']] = $row;
    }
    var_export(array_values($result));
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    [
        ['user_id' => 82, 'ac_type' => 2], // was input row [3]
        ['user_id' => 80, 'ac_type' => 5], // was input row [4]
        ['user_id' => 76, 'ac_type' => 1]  // was input row [2]
    ]
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用条件或空合并赋值运算符保留第一个出现的行,同时删除重复项。

    foreach null 合并赋值演示

    foreach ($array as $a) {
        $result[$a['user_id']] ??= $a; // only store if first occurrence of user_id
    }
    var_export(array_values($result)); // re-index and print
    
    Run Code Online (Sandbox Code Playgroud)

    foreach isset 演示

    foreach ($array as $a) {
        if (!isset($result[$a['user_id']])) {
            $result[$a['user_id']] = $a; // only store if first occurrence of user_id
        }
    }
    var_export(array_values($result)); // re-index and print
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    [
        ['user_id' => 82, 'ac_type' => 1], // was input row [0]
        ['user_id' => 80, 'ac_type' => 5], // was input row [1]
        ['user_id' => 76, 'ac_type' => 1]  // was input row [2]
    ]
    
    Run Code Online (Sandbox Code Playgroud)
  3. 也可以无条件推送数据并避免条件,但输入和输出之间的行顺序可能不同(如果这对您很重要)。

    array_reverse、array_column 演示

    var_export(array_values(array_column(array_reverse($array), null, 'user_id')));
    
    Run Code Online (Sandbox Code Playgroud)

    array_reduce 演示

    var_export(
        array_values(
            array_reduce(
                $array,
                fn($res, $row) => array_replace([$row['user_id'] => $row], $res),
                []
            )
        )
    );
    
    Run Code Online (Sandbox Code Playgroud)

    foreach array_reverse 演示

    $result = [];
    foreach (array_reverse($array) as $row) {
        $result[$row['user_id']] = $row;
    }
    var_export(array_values($result));
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    [
        ['user_id' => 80, 'ac_type' => 5], // was input row [1]
        ['user_id' => 82, 'ac_type' => 1], // was input row [0]
        ['user_id' => 76, 'ac_type' => 1]  // was input row [2]
    ]
    
    Run Code Online (Sandbox Code Playgroud)

关于本示例中未表达的边缘情况的警告:如果您使用行值作为标识符,而这些标识符在用作键时可能会被损坏,则上述技术将给出不可靠的结果。例如,PHP 不允许将浮点值作为键(它们将导致错误或被截断,具体取决于您的 PHP 版本)。只有在这些边缘情况下,您才可能考虑使用低效的迭代调用in_array()来评估唯一性。


array_unique(..., SORT_REGULAR)仅当通过整行数据确定唯一性时才适合使用。

array_unique 演示

var_export(array_unique($array, SORT_REGULAR));
Run Code Online (Sandbox Code Playgroud)

输出:

[
    ['user_id' => 82, 'ac_type' => 1], // was input row [0]
    ['user_id' => 80, 'ac_type' => 5], // was input row [1]
    ['user_id' => 76, 'ac_type' => 1]  // was input row [2]
    ['user_id' => 82, 'ac_type' => 2], // was input row [3]
]
Run Code Online (Sandbox Code Playgroud)

作为需求的稍微扩展,如果必须基于多个列而不是所有列来确定唯一性,则使用由有意义的列值组成的“复合键”。下面使用空合并赋值运算符,但也可以实现 #2 和 #3 中的其他技术。

代码:(演示

foreach ($array as $row) {
    $compositeKey = $row['user_id'] . '_' . $row['ac_type'];
    $result[$compositeKey] ??= $row;      // only store if first occurrence of compositeKey
}
Run Code Online (Sandbox Code Playgroud)

虽然我从未使用过它,但Ouzo Goodies 库似乎有一个uniqueBy()与该主题相关的方法。请参阅此处未解释的片段。