Filtrar/eliminar filas donde el valor de la columna se encuentra más de una vez en una matriz multidimensional

Resuelto Roxx asked hace 54 años • 4 respuestas

Necesito eliminar filas de mi matriz de entrada donde aparecen valores duplicados en una columna específica.

Matriz de muestra:

$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]
];

Me gustaría filtrar para user_idgarantizar la singularidad y lograr este resultado:

Entonces, mi salida será así:

[
    ['user_id' => 82, 'ac_type' => 1],
    ['user_id' => 80, 'ac_type' => 5],
    ['user_id' => 76, 'ac_type' => 1]
]

Ya encontré esta página , pero ninguna de las respuestas funciona para mi situación:

$result = array_unique($array, SORT_REGULAR);

y

$result = array_map("unserialize", array_unique(array_map("serialize", $array)));

y

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

pero todavía existen filas duplicadas.

Roxx avatar Jan 01 '70 08:01 Roxx
Aceptado

Para obtener un "ejemplo mínimo, completo y verificable" más claro, usaré la siguiente matriz de entrada en mis demostraciones:

$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
  1. Inserte filas incondicionalmente en una matriz de resultados y asigne claves asociativas de primer nivel, luego vuelva a indexar con array_values(). Este enfoque sobrescribe las filas duplicadas anteriores con las que aparecen más tarde.

    demostración de array_column :

    var_export(array_values(array_column($array, null, 'user_id')));
    

    demostración foreach :

    $result = [];
    foreach ($array as $row) {
        $result[$row['user_id']] = $row;
    }
    var_export(array_values($result));
    

    Producción:

    [
        ['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]
    ]
    
  2. Utilice una condición o el operador de asignación coalescente nula para preservar la primera fila que aparece mientras elimina los duplicados.

    Demostración de asignación de fusión nula de foreach :

    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
    

    demostración de isset foreach :

    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
    

    Producción:

    [
        ['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]
    ]
    
  3. También es posible enviar datos incondicionalmente Y evitar una condición, pero el orden de las filas puede diferir entre la entrada y la salida (si es importante para usted).

    demostración de array_reverse, array_column :

    var_export(array_values(array_column(array_reverse($array), null, 'user_id')));
    

    demostración de array_reduce :

    var_export(
        array_values(
            array_reduce(
                $array,
                fn($res, $row) => array_replace([$row['user_id'] => $row], $res),
                []
            )
        )
    );
    

    Demostración de foreach array_reverse :

    $result = [];
    foreach (array_reverse($array) as $row) {
        $result[$row['user_id']] = $row;
    }
    var_export(array_values($result));
    

    Producción:

    [
        ['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]
    ]
    

Una advertencia sobre un caso marginal que no se expresa en este ejemplo: si utiliza valores de fila como identificadores que pueden dañarse al usarse como claves, las técnicas anteriores darán resultados poco confiables. Por ejemplo, PHP no permite valores flotantes como claves (provocarán un error o se truncarán, dependiendo de su versión de PHP). Solo en estos casos marginales podría considerar el uso de llamadas iteradas e ineficientes in_array()para evaluar la unicidad.


El uso array_unique(..., SORT_REGULAR)solo es adecuado para determinar la unicidad de filas COMPLETAS de datos.

demostración array_unique :

var_export(array_unique($array, SORT_REGULAR));

Producción:

[
    ['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]
]

Como una ligera extensión de los requisitos, si se debe determinar la unicidad en función de más de una columna, pero no de todas las columnas, utilice una "clave compuesta" compuesta por los valores de columna significativos. Lo siguiente utiliza el operador de asignación coalescente nula, pero también se pueden implementar las otras técnicas de los puntos 2 y 3.

Código: ( Demostración )

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

Aunque nunca lo he usado, la biblioteca Ouzo Goodies parece tener un uniqueBy()método relevante para este tema. Vea el fragmento inexplicable aquí .

mickmackusa avatar Aug 10 '2017 04:08 mickmackusa
$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'=>6]
];

$array = array_reverse($array);

$v = array_reverse( 
    array_values( 
        array_combine( 
            array_column($array, 'user_id'),
            $array
        )
    )
);


echo '<pre>';
var_dump($v);

Resultado:

array(3) {
  [0]=>
  array(2) {
    ["user_id"]=>
    int(76)
    ["ac_type"]=>
    int(1)
  }
  [1]=>
  array(2) {
    ["user_id"]=>
    int(82)
    ["ac_type"]=>
    int(1)
  }
  [2]=>
  array(2) {
    ["user_id"]=>
    int(80)
    ["ac_type"]=>
    int(5)
  }
}
Thanh Nguyen avatar Aug 10 '2017 04:08 Thanh Nguyen