PHP: Wie verwende ich array_filter (), um Array-Schlüssel zu filtern?


363

Die Rückruffunktion in array_filter()übergibt nur die Werte des Arrays, nicht die Schlüssel.

Wenn ich habe:

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

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

Was ist der beste Weg, um alle Schlüssel zu löschen $my_array, die sich nicht im $allowedArray befinden?

Gewünschte Ausgabe:

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

Keine Lösung , sondern ein anderer Ansatz , die nützlich sein könnte , ist zu $b = ['foo' => $a['foo'], 'bar' => $a['bar']]Dies wird zur Folge haben $b['bar']sei null.
Oriadam

Antworten:


322

PHP 5.6 eingeführt einen dritten Parameter array_filter(), flag, dass Sie einstellen können , ARRAY_FILTER_USE_KEYum Filter mit Schlüssel statt Wert:

$my_array = ['foo' => 1, 'hello' => 'world'];
$allowed  = ['foo', 'bar'];
$filtered = array_filter(
    $my_array,
    function ($key) use ($allowed) {
        return in_array($key, $allowed);
    },
    ARRAY_FILTER_USE_KEY
);

Dies ist array_intersect_key($my_array, array_flip($allowed))natürlich nicht so elegant wie , bietet jedoch die zusätzliche Flexibilität, einen beliebigen Test für den Schlüssel durchzuführen, z. B. $allowedkönnte Regex-Muster anstelle von einfachen Zeichenfolgen enthalten.

Sie können auch ARRAY_FILTER_USE_BOTHden Wert und den Schlüssel an Ihre Filterfunktion übergeben lassen. Hier ist ein erfundenes Beispiel, das auf dem ersten basiert, aber beachten Sie, dass ich nicht empfehlen würde, Filterregeln auf $alloweddiese Weise zu codieren :

$my_array = ['foo' => 1, 'bar' => 'baz', 'hello' => 'wld'];
$allowed  = ['foo' => true, 'bar' => true, 'hello' => 'world'];
$filtered = array_filter(
    $my_array,
    function ($val, $key) use ($allowed) { // N.b. $val, $key not $key, $val
        return isset($allowed[$key]) && (
            $allowed[$key] === true || $allowed[$key] === $val
        );
    },
    ARRAY_FILTER_USE_BOTH
); // ['foo' => 1, 'bar' => 'baz']

21
Verdammt, als Autor dieses Features hätte ich nach dieser Frage
suchen sollen ;-)

1
Danke, das ist besser alsarray_intersect
brzuchal

461

Mit array_intersect_keyund array_flip:

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

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

1
Ich bin gespannt, ob dies effizienter ist als meine Lösung. Es ist definitiv eleganter :)
GWW

13
Dies ist keine allgemeine Lösung, da jeder Wert eindeutig sein muss. Edit: sorry .. ich habe die lösung falsch verstanden. Das Umdrehen der erlaubten Schlüssel ist eine gute Lösung (+1)
Matthew

@ GWW: Ich weiß nicht, ob es effizienter ist, TBH. @konforce: Ich bin mir nicht sicher, ob ich deinen Standpunkt verstehe. Es können nicht zwei identische Schlüssel in einem Array vorhanden sein, daher werden nur Schlüssel in $ my_array zurückgegeben, die in $ allow vorhanden sind.
Vincent Savard

1
Oder verwenden Sie einfach ARRAY_FILTER_USE_KEY: P
Julien Palard

1
Warum verwenden array_flip? Definieren Sie einfach die $allowedmit Tasten:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.

43

Ich musste dasselbe tun, aber mit einem komplexeren array_filterSchlüssel.

Hier ist, wie ich es gemacht habe, mit einer ähnlichen Methode.

// Filter out array elements with keys shorter than 4 characters
$a = array(
  0      => "val 0", 
  "one"  => "val one", 
  "two"  => "val two", 
  "three"=> "val three", 
  "four" => "val four", 
  "five" => "val five", 
  "6"    => "val 6"
); 

$f = array_filter(array_keys($a), function ($k){ return strlen($k)>=4; }); 
$b = array_intersect_key($a, array_flip($f));
print_r($b);

Dies gibt das Ergebnis aus:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)

8

Hier ist eine flexiblere Lösung mit einem Verschluss:

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

Ausgänge:

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

In der Funktion können Sie also andere spezifische Tests durchführen.


1
Ich würde das nicht gerade "flexibler" nennen; es fühlt sich auch viel weniger einfach an als die akzeptierte Lösung.
Maček

Genau. Es wäre flexibler, wenn die Bedingung komplexer wäre.
COil

1
Nur vorbeigehen, für andere Benutzer: Diese Lösung behandelt nicht den Fall, dass das $ my_array doppelte Werte oder Werte enthält, die keine Ganzzahlen oder Zeichenfolgen sind. Ich würde diese Lösung also nicht verwenden.
user23127

2
Ich bin damit einverstanden, dass dies flexibler ist, da Sie die Filterlogik ändern können. Zum Beispiel habe ich ein Array von nicht zugelassenen Schlüsseln verwendet und einfach! In_array ($ key, $ disallowed) zurückgegeben.
Nfplee

5

Wenn Sie nach einer Methode suchen, um ein Array nach einer in Schlüsseln vorkommenden Zeichenfolge zu filtern, können Sie Folgendes verwenden:

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

Das Ergebnis von print_r($mResult)ist

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

Eine Anpassung dieser Antwort, die reguläre Ausdrücke unterstützt

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"));

Ausgabe

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

Danke für deine Antwort. Ich möchte Ihnen mitteilen, dass die Verwendung stristrinnerhalb der "Arbeit" der Funktion einige Annahmen für den Endbenutzer macht. Vielleicht wäre es besser, dem Benutzer zu erlauben, einen regulären Ausdruck einzugeben; Dies würde ihnen mehr Flexibilität in Bezug auf bestimmte Dinge wie Anker, Wortgrenzen und Groß- und Kleinschreibung usw. geben
maček

Ich habe eine Anpassung Ihrer Antwort hinzugefügt, die anderen Menschen helfen könnte
maček

1
Sie haben sicherlich Recht, maček, das ist ein vielseitigerer Ansatz für Benutzer, die mit Regex vertraut sind. Vielen Dank.
Nicolas Zimmer

5

So erhalten Sie den aktuellen Schlüssel eines Arrays bei Verwendung array_filter

Unabhängig davon, wie ich Vincents Lösung für Mačeks Problem mag, wird sie nicht wirklich verwendet array_filter. Wenn Sie von einer Suchmaschine hierher gekommen sind, haben Sie vielleicht nach so etwas gesucht ( PHP> = 5.3 ):

$array = ['apple' => 'red', 'pear' => 'green'];
reset($array); // Unimportant here, but make sure your array is reset

$apples = array_filter($array, function($color) use ($&array) {
  $key = key($array);
  next($array); // advance array pointer

  return key($array) === 'apple';
}

Es übergibt das Array, das Sie filtern, als Referenz auf den Rückruf. Da array_filterdas Array herkömmlicherweise nicht durch Erhöhen des öffentlichen internen Zeigers durchlaufen wird, müssen Sie es selbst weiterentwickeln.

Wichtig ist hierbei, dass Sie sicherstellen müssen, dass Ihr Array zurückgesetzt wird, da Sie sonst möglicherweise genau in der Mitte beginnen.

In PHP> = 5.4 können Sie den Rückruf noch kürzer machen:

$apples = array_filter($array, function($color) use ($&array) {
  return each($array)['key'] === 'apple';
}

3

Hier ist eine weniger flexible Alternative mit unset () :

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

Das Ergebnis des print_r($array)Seins:

Array
(
    [2] => two
)

Dies gilt nicht, wenn Sie die gefilterten Werte für die spätere Verwendung aufbewahren möchten, aber aufgeräumter, wenn Sie sicher sind, dass dies nicht der Fall ist .


1
Sie sollten überprüfen, ob der Schlüssel $ key im Array $ vorhanden ist, bevor Sie ihn deaktivieren.
Jarek Jakubowski

3
@JarekJakubowski Sie müssen bei der Verwendung nicht überprüfen, ob ein Array-Schlüssel vorhanden ist unset(). Es werden keine Warnungen ausgegeben, wenn der Schlüssel nicht vorhanden ist.
Christopher

3

Ab PHP 5.6 können Sie das ARRAY_FILTER_USE_KEYFlag verwenden in array_filter:

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


Andernfalls können Sie diese Funktion ( von TestDummy ) verwenden:

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);
});


Und hier ist eine erweiterte Version von mir, die einen Rückruf oder direkt die Schlüssel akzeptiert:

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));


Zu guter Letzt können Sie auch ein einfaches verwenden foreach:

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

1

Vielleicht ein Overkill, wenn Sie es nur einmal brauchen, aber Sie können die YaLinqo- Bibliothek * verwenden, um Sammlungen zu filtern (und andere Transformationen durchzuführen). Diese Bibliothek ermöglicht die Durchführung von SQL-ähnlichen Abfragen für Objekte mit fließender Syntax. Seine whereFunktion akzeptiert einen Calback mit zwei Argumenten: einem Wert und einem Schlüssel. Zum Beispiel:

$filtered = from($array)
    ->where(function ($v, $k) use ($allowed) {
        return in_array($k, $allowed);
    })
    ->toArray();

(Die whereFunktion gibt einen Iterator zurück. Wenn Sie also nur foreacheinmal mit der resultierenden Sequenz iterieren müssen ,->toArray() kann diese entfernt werden.)

* von mir entwickelt


1

Array Filter Funktion von PHP:

array_filter ( $array, $callback_function, $flag )

$ array - Dies ist das Eingabearray

$ callback_function - Die zu verwendende Rückruffunktion , Wenn die Rückruffunktion true zurückgibt , wird der aktuelle Wert aus dem Array in das Ergebnisarray zurückgegeben.

$ flag - Dies ist ein optionaler Parameter , der bestimmt, welche Argumente an die Rückruffunktion gesendet werden. Wenn dieser Parameter leer ist, verwendet die Rückruffunktion Array-Werte als Argument. Wenn Sie einen Array-Schlüssel als Argument senden möchten, verwenden Sie $ flag als ARRAY_FILTER_USE_KEY . Wenn Sie sowohl Schlüssel als auch Werte senden möchten, sollten Sie $ flag als ARRAY_FILTER_USE_BOTH verwenden .

Zum Beispiel: Betrachten Sie ein einfaches Array

$array = array("a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5);

Wenn Sie ein Array basierend auf dem Array-Schlüssel filtern möchten , müssen Sie ARRAY_FILTER_USE_KEY als dritten Parameter der Array-Funktion array_filter verwenden.

$get_key_res = array_filter($array,"get_key",ARRAY_FILTER_USE_KEY );

Wenn Sie das Array basierend auf dem Array-Schlüssel und dem Array-Wert filtern möchten , müssen Sie ARRAY_FILTER_USE_BOTH als dritten Parameter der Array-Funktion array_filter verwenden.

$get_both = array_filter($array,"get_both",ARRAY_FILTER_USE_BOTH );

Beispiel für Rückruffunktionen:

 function get_key($key)
 {
    if($key == 'a')
    {
        return true;
    } else {
        return false;
    }
}
function get_both($val,$key)
{
    if($key == 'a' && $val == 1)
    {
        return true;
    }   else {
        return false;
    }
}

Es wird ausgegeben

Output of $get_key is :Array ( [a] => 1 ) 
Output of $get_both is :Array ( [a] => 1 ) 

0

Mit dieser Funktion können Sie ein mehrdimensionales Array filtern

function filter_array_keys($array,$filter_keys=array()){

    $l=array(&$array);
    $c=1;
    //This first loop will loop until the count var is stable//
    for($r=0;$r<$c;$r++){
        //This loop will loop thru the child element list//
        $keys = array_keys($l[$r]);

        for($z=0;$z<count($l[$r]);$z++){
            $object = &$l[$r][$keys[$z]];

            if(is_array($object)){
                $i=0;
                $keys_on_array=array_keys($object);
                $object=array_filter($object,function($el) use(&$i,$keys_on_array,$filter_keys){
                    $key = $keys_on_array[$i];
                    $i++;

                    if(in_array($key,$filter_keys) || is_int($key))return false;                
                    return true;                        
                });
            }

            if(is_array($l[$r][$keys[$z]])){
                $l[] = &$l[$r][$keys[$z]];
                $c++;
            }//IF           
        }//FOR
    }//FOR  

    return $l[0];

}

0
// Filter out array elements with keys shorter than 4 characters 
// By using Anonymous function with  Closure...     

function comparison($min)
{
   return function($item) use ($min) { 
      return strlen($item) >= $min;   
   }; 
}

$input = array(
  0      => "val 0",
  "one"  => "val one",
  "two"  => "val two",
  "three"=> "val three",
  "four" => "val four",  
  "five" => "val five",    
  "6"    => "val 6"    
);

$output = array_filter(array_keys($input), comparison(4));    

print_r($output);

Ausgabe vom Lauf


0

Naive und hässliche (scheint aber schneller zu sein) Lösung?

Ich habe dies nur in PHP 7.3.11 versucht, aber eine hässliche Schleife scheint in etwa einem Drittel der Zeit ausgeführt zu werden. Ähnliche Ergebnisse auf einem Array mit einigen hundert Schlüsseln. Mikrooptimierung, wahrscheinlich nicht nützlich in RW, fand sie aber überraschend und interessant:

$time = microtime(true);
$i = 100000;
while($i) {
    $my_array = ['foo' => 1, 'hello' => 'world'];
    $allowed  = ['foo', 'bar'];
    $filtered = array_filter(
        $my_array,
        function ($key) use ($allowed) {
            return in_array($key, $allowed);
        },
        ARRAY_FILTER_USE_KEY
    );
    $i--;
}
print_r($filtered);
echo microtime(true) - $time . ' on array_filter';

// 0.40600109100342 on array_filter
$time2 = microtime(true);
$i2 = 100000;
while($i2) {
    $my_array2 = ['foo' => 1, 'hello' => 'world'];
    $allowed2  = ['foo', 'bar'];
    $filtered2 = [];
    foreach ($my_array2 as $k => $v) {
        if (in_array($k, $allowed2)) $filtered2[$k] = $v;
    }
    $i2--;
}
print_r($filtered2);
echo microtime(true) - $time2 . ' on ugly loop';
// 0.15677785873413 on ugly loop

-1
$elements_array = ['first', 'second'];

Funktion zum Entfernen einiger Array-Elemente

function remove($arr, $data) {
    return array_filter($arr, function ($element) use ($data) {
        return $element != $data;
    });
}

anrufen und drucken

print_r(remove($elements_array, 'second'));

das Ergebnis Array ( [0] => first )


Die Frage betraf das Filtern von Array-Schlüsseln und nicht von Werten.
Poletaew
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.