¿Cómo filtrar una matriz asociativa comparando claves con valores en una matriz indexada?
La función de devolución de llamada 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 $my_array
que no están en la $allowed
matriz?
Salida deseada:
$my_array = array("foo" => 1);
Con array_intersect_key
y array_flip
:
var_dump(array_intersect_key($my_array, array_flip($allowed)));
array(1) {
["foo"]=>
int(1)
}
PHP 5.6 introdujo un tercer parámetro en array_filter()
, flag
que puede configurar 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 conciso:
$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 puede utilizar ARRAY_FILTER_USE_BOTH
para pasar tanto el valor como la clave 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 de $allowed
esta manera:
$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']
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, puedes hacer otras pruebas específicas.
Aquí hay una alternativa menos flexible usando unset() :
$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)
ser:
Array
(
[2] => two
)
Esto no se aplica si desea conservar los valores filtrados para su uso posterior, pero de manera más ordenada, si está seguro de que no es así.
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
)