PHP:array_filter()を使用して配列キーをフィルタリングする方法


363

のコールバック関数 array_filter()は、キーではなく配列の値のみを渡します。

私が持っている場合:

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

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

すべてのキーを削除する最良の方法は何ですか $my_array$allowed配列にないですか?

望ましい出力:

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

ないソリューションが、役に立つかもしれない別のアプローチがあるに$b = ['foo' => $a['foo'], 'bar' => $a['bar']]になります。この$b['bar']BE null
oriadam

回答:


322

PHP 5.6では、3番目のパラメーターがarray_filter()、に導入され、値の代わりにキーでフィルターするflagようARRAY_FILTER_USE_KEYに設定できます。

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

明らかにこれはほどエレガントではありませんarray_intersect_key($my_array, array_flip($allowed))が、キーに対して任意のテストを実行するという追加の柔軟性を提供します。たとえば$allowed、プレーンな文字列の代わりに正規表現パターンを含めることができます。

を使用ARRAY_FILTER_USE_BOTHして、値とキーの両方をフィルター関数に渡すこともできます。これは最初のものに基づいた不自然な例ですが、$allowedこの方法でフィルタリングルールをエンコードすることはお勧めしません。

$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
くそー、と著者、その機能の私はこの質問を探している必要があります;-)
ジャック

1
おかげで、これはより優れていますarray_intersect
brzuchal 2016

461

array_intersect_keyarray_flip

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

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

1
これが私のソリューションよりも効率的であるかどうか私は興味がありますか?それは間違いなくよりエレガントです:)
GWW

13
各値が一意であることを要求するため、これは一般的なソリューションではありません。編集:申し訳ありません。私は解決策を誤って読みました。許可されたキーをめくることは良い解決策です(+1)
Matthew

@GWW:TBHの方が効率的かどうかわかりません。@konforce:私はあなたの主張を理解するのがよくわかりません。配列内に2つの同一のキーを含めることはできないため、$ allowedに存在する$ my_array内のキーのみを返します。
Vincent Savard、2010年

1
P:または単にARRAY_FILTER_USE_KEY使用
ジュリアンPalard

1
なぜ使用array_flip?単純に定義する$allowedキーで:allowed = array ( 'foo' => 1, 'bar' => 1 );
のYuval A.

43

私も同じことをする必要がありarray_filterましたが、キーがより複雑になりました。

同様の方法を使用して、これを実行しました。

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

これは結果を出力します:

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

8

クロージャーを使用したより柔軟なソリューションを次に示します。

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

出力:

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

したがって、関数では、他の特定のテストを実行できます。


1
これを「より柔軟」と正確に呼ぶことはしません。また、受け入れられている解決策よりもずっと簡単ではないと感じています。
マセック

同意する。条件がより複雑なものであれば、より柔軟になります。
コイル2013年

1
ただ通り過ぎて、他のユーザーのために:この解決策は、$ my_arrayに重複した値や整数や文字列ではない値がある場合を扱いません。したがって、私はこのソリューションを使用しません。
user23127 14年

2
フィルターロジックを変更できるため、これはより柔軟です。たとえば、許可されていないキーの配列を使用して、単に!in_array($ key、$ disallowed)を返しました。
nfplee 2014

5

キーに出現する文字列で配列をフィルタリングする方法を探している場合は、次の方法を使用できます。

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

の結果print_r($mResult)

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

正規表現をサポートするこの回答の改作

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

出力

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

ご回答有難うございます。stristr関数の「作業」内で使用することは、エンドユーザーに対していくつかの仮定を行うことになることをお伝えします。おそらく、ユーザーが正規表現を渡せるようにした方がいいでしょう。これにより、アンカー、単語の境界、大文字と小文字の区別など、
特定の項目に対する柔軟性が高まります

私は他の人々を助けるかもしれないあなたの答えの適応追加しました
マセック

1
確かにそうです、maček。これは、正規表現に慣れているユーザーにとっては、より用途の広いアプローチです。ありがとう。
Nicolas Zimmer

5

使用時に配列の現在のキーを取得する方法 array_filter

Mačekの問題に対するVincentのソリューションがどのように好きかに関わらず、実際にはを使用しませんarray_filter。あなたが検索エンジンからここに来たなら、あなたはおそらくこのようなものを探しているでしょう(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';
}

フィルタリングしている配列をコールバックへの参照として渡します。なのでarray_filter従来、それはあなたが自分でそれを前進させるために持っている公共の内部ポインタです増やすことで、配列を反復処理しません。

ここで重要なことは、アレイがリセットされていることを確認する必要があることです。そうでない場合、アレイの真ん中から開始することができます。

PHP> = 5.4あなたは、コールバックより短いを作ることができます。

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

3

unset()を使用した柔軟性の低い代替案を次に示します。

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

の結果print_r($array)

Array
(
    [2] => two
)

これは、後で使用するためにフィルター処理された値を保持したい場合は適用できませんが、そうでない場合は、より整然としています。


1
設定を解除する前に、キー$ keyが$ arrayに存在するかどうかを確認する必要があります。
Jarek Jakubowski

3
@JarekJakubowskiでは、使用時に配列キーが存在するかどうかを確認する必要はありませんunset()。キーが存在しない場合、警告は発行されません。
Christopher

3

PHP 5.6以降では、次の場所でARRAY_FILTER_USE_KEYフラグを使用できますarray_filter

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


それ以外の場合は、この関数を使用できます(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);
});


そして、コールバックまたは直接キーを受け付ける私の拡張バージョンがあります:

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


最後に大事なことを言いますが、シンプルなものを使うこともできますforeach

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

1

一度だけ必要な場合は、やり過ぎかもしれませんが、YaLinqoライブラリ*を使用して、コレクションをフィルター処理(およびその他の変換を実行)できます。このライブラリを使用すると、流暢な構文を持つオブジェクトに対してSQLのようなクエリを実行できます。そのwhere関数は、値とキーの2つの引数を持つcalbackを受け入れます。例えば:

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

(このwhere関数はイテレータを返すためforeach、結果のシーケンスに対して1回だけ反復する必要->toArray()がある場合は削除できます。)

*私が開発


1

PHPの配列フィルター関数:

array_filter ( $array, $callback_function, $flag )

$ array-入力配列です

$ callback_function-使用するコールバック関数。コールバック関数がtrueを返す場合、配列の現在の値が結果配列に返されます。

$ flag-これはオプションのパラメーターであり、コールバック関数に送信される引数を決定します。このパラメーターが空の場合、コールバック関数は配列値を引数として受け取ります。配列キーを引数として送信する場合は、ARRAY_FILTER_USE_KEYとして$ flagを使用します。キーと値の両方を送信する場合は、ARRAY_FILTER_USE_BOTHとして$ flagを使用する必要があります。

例:単純な配列を検討

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

配列キーに基づいて配列をフィルタリングする場合は、配列関数array_filterの3番目のパラメーターとしてARRAY_FILTER_USE_KEYを使用する必要があります

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

配列キーと配列値に基づいて配列をフィルタリングする場合は、配列関数array_filterの3番目のパラメーターとしてARRAY_FILTER_USE_BOTHを使用する必要があります。

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

コールバック関数の例:

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

出力されます

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

0

この関数を使用すると、多次元配列をフィルタリングできます

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

実行からの出力


0

素朴で醜い(しかしより高速に見える)ソリューション?

これをphp 7.3.11で試しただけですが、醜いループが約3分の1の時間で実行されるようです。数百のキーを持つ配列でも同様の結果が得られます。マイクロ最適化は、RWではおそらく役に立たないかもしれませんが、意外で興味深いことがわかりました。

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

一部の配列要素を削除する関数

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

呼び出して印刷

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

結果 Array ( [0] => first )


質問は、値ではなく配列キーをフィルタリングすることでした。
18年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.