¿Cómo filtrar matrices asociativas usando claves de una matriz indexada en PHP?

12 minutos de lectura

avatar de usuario de maček
macek

La función de devolución de llamada en array_filter() solo pasa los valores de la matriz, no las claves.

Si tengo:

$my_array = array("foo" => 1, "hello" => "world");

$allowed = array("foo", "bar");

¿Cuál es la mejor manera de eliminar todas las claves en $my_array que no están en el $allowed ¿formación?

Salida deseada:

$my_array = array("foo" => 1);

  • No es una solución, pero otro enfoque que podría ser útil es $b = ['foo' => $a['foo'], 'bar' => $a['bar']] Esto resultará en $b['bar'] ser null.

    – oriadam

    02/04/2018 a las 13:45


Avatar de usuario de Vincent Savard
Vicente Savard

Con array_intersect_key y array_flip:

var_dump(array_intersect_key($my_array, array_flip($allowed)));

array(1) {
  ["foo"]=>
  int(1)
}

  • Sin embargo, tengo curiosidad si esto es más eficiente que mi solución. Definitivamente es más elegante 🙂

    – GWW

    23 de noviembre de 2010 a las 19:48

  • @GWW, en general, descubrí que este tipo de funciones de matriz son más rápidas que las equivalentes foreach bucle (y a veces considerablemente), pero la única forma de saberlo con certeza es cronometrarlos a ambos con los mismos datos.

    – Mateo

    23 de noviembre de 2010 a las 20:03

  • Por que usar array_flip? Simplemente define el $allowed con llaves: allowed = array ( 'foo' => 1, 'bar' => 1 );

    – Yuval A.

    04/06/2017 a las 12:55

Avatar de usuario de Richard Turner
ricardo turner

PHP 5.6 introdujo un tercer parámetro para array_filter(), flagque puede configurar para ARRAY_FILTER_USE_KEY para filtrar por clave en lugar de valor:

$my_array = ['foo' => 1, 'hello' => 'world'];
$allowed  = ['foo', 'bar'];
$filtered = array_filter(
    $my_array,
    function ($key) use ($allowed) {
        // N.b. in_array() is notorious for being slow 
        return in_array($key, $allowed);
    },
    ARRAY_FILTER_USE_KEY
);

Dado que PHP 7.4 introdujo funciones de flecha, podemos hacer esto más breve:

$my_array = ['foo' => 1, 'hello' => 'world'];
$allowed  = ['foo', 'bar'];
$filtered = array_filter(
    $my_array,
    fn ($key) => in_array($key, $allowed),
    ARRAY_FILTER_USE_KEY
);

Claramente esto no es tan elegante como array_intersect_key($my_array, array_flip($allowed))pero ofrece la flexibilidad adicional de realizar una prueba arbitraria contra la clave, por ejemplo $allowed podría contener patrones de expresiones regulares en lugar de cadenas simples.

También puedes usar ARRAY_FILTER_USE_BOTH para que tanto el valor como la clave pasen a su función de filtro. Aquí hay un ejemplo artificial basado en el primero, pero tenga en cuenta que no recomendaría codificar reglas de filtrado usando $allowed Por aquí:

$my_array = ['foo' => 1, 'bar' => 'baz', 'hello' => 'wld'];
$allowed  = ['foo' => true, 'bar' => true, 'hello' => 'world'];
$filtered = array_filter(
    $my_array,
    fn ($val, $key) => isset($allowed[$key]) && (
        $allowed[$key] === true || $allowed[$key] === $val
    ),
    ARRAY_FILTER_USE_BOTH
); // ['foo' => 1, 'bar' => 'baz']

  • Maldita sea, como el autor de esa característica debería haber buscado esta pregunta 😉

    – Jacobo

    8 de junio de 2015 a las 8:09


  • PHP 7.4+ $filtered = array_filter( $my_array, fn ($key) => in_array($key, $allowed), ARRAY_FILTER_USE_KEY );

    – jartaud

    27 de febrero de 2021 a las 2:38


  • Cualquier respuesta que aproveche las llamadas iteradas de in_array() no será más eficaz que la más elegante llamada de array_intersect_key(). Sí, la matriz de búsqueda deberá invertirse una vez, pero debido a que PHP es muy rápido para realizar búsquedas clave (como isset()), Espero in_array() para ser dejado en el polvo en la mayoría de los casos de prueba. Más simple, isset() se ha demostrado una y otra vez que supera en gran medida in_array() en puntos de referencia. El único peligro a tener en cuenta es cuando la técnica de volteo muta el valor, como cuando cambia un valor flotante a una clave, se convierte en un int.

    – mickmackusa

    16 de enero a las 5:45


  • @mickmackusa Es probable que necesite tener una matriz grande para que la diferencia sea significativa para el funcionamiento de su aplicación. Por lo general, la legibilidad triunfa sobre las microoptimizaciones de rendimiento. Sin duda, algo de lo que ser consciente.

    –Richard Turner

    17 de enero a las 9:25

  • No hay características redentoras para sus fragmentos y no usaría ninguno de ellos en mis propios proyectos. La técnica flip&intersect_key de VincentSavard es más eficaz, más concisa, más elegante, más legible y utiliza adecuadamente un enfoque funcional completamente nativo. No te estoy atacando, estoy comparando las publicaciones.

    – mickmackusa

    17 de enero a las 9:43


Aquí hay una solución más flexible usando un cierre:

$my_array = array("foo" => 1, "hello" => "world");
$allowed = array("foo", "bar");
$result = array_flip(array_filter(array_flip($my_array), function ($key) use ($allowed)
{
    return in_array($key, $allowed);
}));
var_dump($result);

Salidas:

array(1) {
  'foo' =>
  int(1)
}

Entonces, en la función, puede hacer otras pruebas específicas.

  • No llamaría exactamente a esto “más flexible”; también se siente mucho menos sencillo que la solución aceptada.

    – macek

    26 de enero de 2013 a las 21:33

  • Estoy de acuerdo. Sería más flexible si la condición fuera más compleja.

    – BOBINA

    31 de enero de 2013 a las 11:25

  • Solo de paso, para otros usuarios: esta solución no se ocupa del caso de que $my_array tenga valores duplicados o valores que no sean enteros o cadenas. Así que no usaría esta solución.

    – usuario23127

    9 de junio de 2014 a las 17:38

  • Estoy de acuerdo en que esto es más flexible ya que le permite cambiar la lógica del filtro. Por ejemplo, utilicé una matriz de claves no permitidas y simplemente devolví !in_array($key, $disallowed).

    – nfplee

    7 sep 2014 a las 13:05


  • es peligroso llamar array_flip($my_array). Si hay valores duplicados en la matriz, el tamaño de la matriz se reducirá porque las matrices no pueden tener claves duplicadas en el mismo nivel. Este enfoque no debe usarse, es inestable/poco confiable.

    – mickmackusa

    18 ene a las 21:33


Aquí hay una alternativa menos flexible usando desarmar():

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three'
);
$disallowed = array(1,3);
foreach($disallowed as $key){
    unset($array[$key]);
}

El resultado de print_r($array) siendo:

Array
(
    [2] => two
)

Esto no es aplicable si desea mantener el filtrado valores para su uso posterior pero más ordenado, si está seguro de que no lo hace.

Avatar de usuario de Nicolas Zimmer
Nicolás Zimmer

Si está buscando un método para filtrar una matriz por una cadena que aparece en las claves, puede usar:

$mArray=array('foo'=>'bar','foo2'=>'bar2','fooToo'=>'bar3','baz'=>'nope');
$mSearch="foo";
$allowed=array_filter(
    array_keys($mArray),
    function($key) use ($mSearch){
        return stristr($key,$mSearch);
    });
$mResult=array_intersect_key($mArray,array_flip($allowed));

El resultado de print_r($mResult) es

Array ( [foo] => bar [foo2] => bar2 [fooToo] => bar3 )

Una adaptación de esta respuesta que admite expresiones regulares.

function array_preg_filter_keys($arr, $regexp) {
  $keys = array_keys($arr);
  $match = array_filter($keys, function($k) use($regexp) {
    return preg_match($regexp, $k) === 1;
  });
  return array_intersect_key($arr, array_flip($match));
}

$mArray = array('foo'=>'yes', 'foo2'=>'yes', 'FooToo'=>'yes', 'baz'=>'nope');

print_r(array_preg_filter_keys($mArray, "/^foo/i"));

Producción

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)

  • gracias por tu respuesta. Yo le diría que usando stristr dentro del “trabajo” de la función está hacer algunas suposiciones para el usuario final. Quizás sería mejor permitir que el usuario pase una expresión regular; esto les daría más flexibilidad sobre ciertas cosas como anclas, límites de palabras y mayúsculas y minúsculas, etc.

    – macek

    02/03/2014 a las 19:51

  • He agregado una adaptación de tu respuesta que podría ayudar a otras personas.

    – macek

    02/03/2014 a las 20:05

  • Ciertamente tiene razón, maček, ese es un enfoque más versátil para los usuarios que se sienten cómodos con expresiones regulares. Gracias.

    – Nicolás Zimmer

    3 de marzo de 2014 a las 8:02

  • Esta es la respuesta correcta a una pregunta diferente. Elimine todos los elementos de la matriz que no comiencen con una determinada cadena. Su respuesta ignora los requisitos de la pregunta formulada.

    – mickmackusa

    17 de enero a las 10:19

A partir de PHP 5.6, puede utilizar el ARRAY_FILTER_USE_KEY bandera en array_filter:

$result = array_filter($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
}, ARRAY_FILTER_USE_KEY);

De lo contrario, puede utilizar esta función (de TestDummy):

function filter_array_keys(array $array, $callback)
{
    $matchedKeys = array_filter(array_keys($array), $callback);

    return array_intersect_key($array, array_flip($matchedKeys));
}

$result = filter_array_keys($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
});

Y aquí hay una versión aumentada mía, que acepta una devolución de llamada o directamente las teclas:

function filter_array_keys(array $array, $keys)
{
    if (is_callable($keys)) {
        $keys = array_filter(array_keys($array), $keys);
    }

    return array_intersect_key($array, array_flip($keys));
}

// using a callback, like array_filter:
$result = filter_array_keys($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
});

// or, if you already have the keys:
$result = filter_array_keys($my_array, $allowed));

Por último, pero no menos importante, también puede usar un simple foreach:

$result = [];
foreach ($my_array as $key => $value) {
    if (in_array($key, $allowed)) {
        $result[$key] = $value;
    }
}

  • gracias por tu respuesta. Yo le diría que usando stristr dentro del “trabajo” de la función está hacer algunas suposiciones para el usuario final. Quizás sería mejor permitir que el usuario pase una expresión regular; esto les daría más flexibilidad sobre ciertas cosas como anclas, límites de palabras y mayúsculas y minúsculas, etc.

    – macek

    02/03/2014 a las 19:51

  • He agregado una adaptación de tu respuesta que podría ayudar a otras personas.

    – macek

    02/03/2014 a las 20:05

  • Ciertamente tiene razón, maček, ese es un enfoque más versátil para los usuarios que se sienten cómodos con expresiones regulares. Gracias.

    – Nicolás Zimmer

    3 de marzo de 2014 a las 8:02

  • Esta es la respuesta correcta a una pregunta diferente. Elimine todos los elementos de la matriz que no comiencen con una determinada cadena. Su respuesta ignora los requisitos de la pregunta formulada.

    – mickmackusa

    17 de enero a las 10:19

Cómo obtener la clave actual de una matriz cuando se usa array_filter

Independientemente de cómo me guste la solución de Vincent para el problema de Maček, en realidad no usa array_filter. Si vino aquí desde un motor de búsqueda y buscaba una forma de acceder a la clave de la iteración actual dentro array_filterDevolución de llamada, tal vez estabas buscando algo como esto (PHP >= 5.3):

$my_array = ["foo" => 1, "hello" => "world"];

$allowed = ["foo", "bar"];

reset($my_array ); // Unnecessary in this case, as we just defined the array, but
                   // make sure your array is reset (see below for further explanation).

$my_array = array_filter($my_array, function($value) use (&$my_array, $allowed) {
  $key = key($my_array); // request key of current internal array pointer
  next($my_array); // advance internal array pointer

  return isset($allowed[$key]);
});

// $my_array now equals ['foo' => 1]

Pasa la matriz que está filtrando como referencia a la devolución de llamada. Como array_filter no itera convencionalmente sobre la matriz al aumentar su puntero interno público, debe avanzarlo usted mismo.

Lo que es importante aquí es que debe asegurarse de que su matriz se restablezca, de lo contrario, podría comenzar justo en el medio (porque el puntero interno de la matriz quedó allí por algún código suyo que se ejecutó antes).

  • Esta respuesta ignora por completo los requisitos del autor de la pregunta y los datos de muestra. Esta respuesta es, en el mejor de los casos, la respuesta correcta a una pregunta diferente… excepto que no lo es. $&array no es PHP válido y each() ha quedado obsoleto desde PHP7.2 y eliminado por completo desde PHP8.

    – mickmackusa

    17 ene a las 10:11

  • Hola @mickmackusa y gracias por tus amables y constructivas palabras. Hace siete años, cuando escribí esta respuesta, PHP 8 ni siquiera estaba en el horizonte y each() no estaba en desuso en absoluto. Imho, la esencia de mi respuesta podría transferirse fácilmente a la pregunta del autor de la pregunta, pero la actualicé en consecuencia, de modo que ahora se puede copiar y pegar sin la necesidad de pensarlo mucho. También arreglé el pequeño error tipográfico con las referencias ($& => &$). Siéntase libre de editar mi respuesta si todavía hay algo que no le gusta. Salud

    – gripe

    20 de enero a las 16:29

  • También tenga en cuenta que esta pregunta se llamó “¿Cómo usar array_filter() para filtrar claves de matriz?” (consulte: stackoverflow.com/posts/4260086/revisions) y se le preguntó, cuando PHP 5.6 no estaba muy extendido, por lo que el nuevo ARRAY_FILTER_USE_KEY la bandera no estaba comúnmente disponible. Todas las respuestas sobre SO son hijas de su tiempo y pueden no ser válidas, precisas o útiles más de media década después. En realidad, no sé si las respuestas ahora obsoletas deben eliminarse o conservarse por razones históricas. Alguien aún podría verse obligado a respaldar un proyecto que utiliza una versión obsoleta de PHP.

    – gripe

    20 de enero a las 16:50


  • Pregúntese, si fuera un investigador que estuviera buscando el “mejor” enfoque para implementar en su aplicación, ¿consideraría que “vale la pena leer” esta respuesta? A veces, su “valor académico” en una respuesta publicada a pesar de que no es óptimo. Si cree que su publicación será útil para futuros investigadores, manténgala aquí. Si cree que agrega una hinchazón innecesaria a una página con 11 respuestas diferentes, ahorre tiempo a los investigadores destrozando la publicación. Incluso las páginas de hace una década necesitan curación en SO, es por eso que superviso las páginas nuevas y antiguas. Me preocupo más que el usuario promedio por nuestro contenido.

    – mickmackusa

    20 de enero a las 21:42


  • Creo que las respuestas obsoletas aún deberían estar disponibles. – para la historia y también para las personas que aún pueden estar usando php ANTIGUO (cualquier idioma) – Las personas que han usado php 5.2, por ejemplo, sabrán cómo adaptar la respuesta a una nueva versión de php.

    – emilushi

    29 de junio a las 18:00


¿Ha sido útil esta solución?