PHP: Làm thế nào để sử dụng Array_filter () để lọc các khóa mảng?


363

Hàm gọi lại array_filter()chỉ truyền vào các giá trị của mảng, không phải các khóa.

Nếu tôi có:

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

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

Cách tốt nhất để xóa tất cả các khóa trong $my_arrayđó không có trong $allowedmảng là gì?

Sản phẩm chất lượng:

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

Không phải là một giải pháp nhưng cách tiếp cận khác có thể hữu ích là để $b = ['foo' => $a['foo'], 'bar' => $a['bar']]này sẽ dẫn đến $b['bar']be null.
oriadam

Câu trả lời:


322

PHP 5.6 giới thiệu một tham số thứ ba array_filter(), flagmà bạn có thể thiết lập để ARRAY_FILTER_USE_KEYđể lọc theo chìa khóa thay vì giá trị:

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

Rõ ràng điều này không thanh lịch như array_intersect_key($my_array, array_flip($allowed)), nhưng nó mang lại sự linh hoạt bổ sung khi thực hiện kiểm tra tùy ý đối với khóa, ví dụ $allowedcó thể chứa các mẫu biểu thức chính quy thay vì các chuỗi đơn giản.

Bạn cũng có thể sử dụng ARRAY_FILTER_USE_BOTHđể có cả giá trị và khóa được truyền cho chức năng lọc của bạn. Đây là một ví dụ giả định dựa trên lần đầu tiên, nhưng lưu ý rằng tôi không khuyến nghị quy tắc lọc mã hóa theo cách $allowednày:

$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
Chết tiệt, với tư cách là tác giả của tính năng đó, đáng lẽ tôi phải tìm câu hỏi này ;-)
Ja͢ck

1
Cảm ơn, điều này tốt hơnarray_intersect
brzuchal 4/2/2016

461

Với array_intersect_keyarray_flip:

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

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

1
Tôi tò mò liệu điều này có hiệu quả hơn giải pháp của tôi không? Nó chắc chắn thanh lịch hơn :)
GWW

13
Đây không phải là một giải pháp chung vì nó sẽ bắt buộc mỗi giá trị là duy nhất. Chỉnh sửa: xin lỗi .. tôi đọc sai giải pháp. Lật các phím được phép là một giải pháp tốt (+1)
Matthew

@GWW: Tôi không biết nếu nó hiệu quả hơn, TBH. @konforce: Tôi không chắc chắn để hiểu quan điểm của bạn. Không thể có hai khóa giống nhau trong một mảng, vì vậy nó sẽ chỉ trả về các khóa trong $ my_array có trong $ được phép.
Vincent Savard

1
Hoặc chỉ cần sử dụng ARRAY_FILTER_USE_KEY: P
Julien Palard

1
Tại sao nên sử dụng array_flip? Chỉ cần xác định các $allowedphím có:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.

43

Tôi cần phải làm như vậy, nhưng với array_filtercác phím phức tạp hơn .

Đây là cách tôi đã làm, sử dụng một phương pháp tương tự.

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

Điều này đưa ra kết quả:

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

8

Đây là một giải pháp linh hoạt hơn bằng cách sử dụng bao đóng:

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

Đầu ra:

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

Vì vậy, trong chức năng, bạn có thể làm các xét nghiệm cụ thể khác.


1
Tôi sẽ không gọi chính xác là "linh hoạt hơn"; nó cũng cảm thấy ít đơn giản hơn nhiều so với giải pháp được chấp nhận.
maček

Tôi đồng ý. Nó sẽ linh hoạt hơn là điều kiện là một điều phức tạp hơn.
COil

1
Chỉ cần đi ngang qua, đối với những người dùng khác: Giải pháp này không giải quyết trường hợp $ my_array có các giá trị trùng lặp hoặc giá trị không phải là số nguyên hoặc chuỗi. Vì vậy, tôi sẽ không sử dụng giải pháp này.
dùng23127

2
Tôi đồng ý điều này linh hoạt hơn vì nó cho phép bạn thay đổi logic bộ lọc. Ví dụ: tôi đã sử dụng một loạt các khóa không được phép và chỉ cần trả về! In_array ($ key, $ không được phép).
nfplee

5

Nếu bạn đang tìm kiếm một phương thức để lọc một mảng bằng một chuỗi xuất hiện trong các khóa, bạn có thể sử dụng:

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

Kết quả print_r($mResult)

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

Một sự thích ứng của câu trả lời này hỗ trợ các biểu thức chính quy

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

Đầu ra

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

cảm ơn câu trả lời của bạn. Tôi sẽ gửi cho bạn rằng việc sử dụng stristrtrong "công việc" của hàm đang tạo ra một số giả định cho người dùng cuối. Có lẽ sẽ tốt hơn nếu cho phép người dùng vượt qua trong một biểu thức thông thường; điều này sẽ giúp họ linh hoạt hơn đối với một số thứ như neo, ranh giới từ và độ nhạy trường hợp, v.v.
maček

Tôi đã thêm một bản phóng tác cho câu trả lời của bạn có thể giúp đỡ người khác
maček

1
Bạn chắc chắn đúng, maček, đó là một cách tiếp cận linh hoạt hơn cho người dùng thoải mái với regex. Cảm ơn.
Nicolas Zimmer

5

Cách lấy khóa hiện tại của một mảng khi sử dụng array_filter

Bất kể tôi thích giải pháp của Vincent cho vấn đề của Maček như thế nào, nó không thực sự sử dụng array_filter. Nếu bạn đến đây từ một công cụ tìm kiếm, bạn có thể tìm kiếm thứ gì đó như thế này ( 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';
}

Nó vượt qua mảng bạn đang lọc làm tham chiếu cho cuộc gọi lại. Vì array_filterthông thường không lặp đi lặp lại trên mảng bằng cách tăng con trỏ nội bộ công khai, bạn phải tự mình nâng cấp nó.

Điều quan trọng ở đây là bạn cần đảm bảo rằng mảng của bạn được đặt lại, nếu không bạn có thể bắt đầu ngay giữa nó.

Trong PHP> = 5.4 bạn có thể thực hiện cuộc gọi lại thậm chí ngắn hơn:

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

3

Đây là một thay thế kém linh hoạt hơn bằng cách sử dụng unset () :

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

Kết quả của print_r($array)việc:

Array
(
    [2] => two
)

Điều này không áp dụng nếu bạn muốn giữ các giá trị được lọc để sử dụng sau nhưng gọn gàng hơn, nếu bạn chắc chắn rằng bạn không.


1
Bạn nên kiểm tra xem khóa $ key có tồn tại trong mảng $ hay không trước khi thực hiện bỏ đặt.
Jarek Jakubowski

3
@JarekJakubowski bạn không cần kiểm tra xem khóa mảng có tồn tại khi sử dụng không unset(). Không có cảnh báo nào được đưa ra nếu khóa không tồn tại.
Christopher

3

Bắt đầu từ PHP 5.6, bạn có thể sử dụng ARRAY_FILTER_USE_KEYcờ trong array_filter:

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


Nếu không, bạn có thể sử dụng chức năng này ( từ 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);
});


Và đây là phiên bản tăng cường của tôi, chấp nhận gọi lại hoặc trực tiếp các phím:

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


Cuối cùng nhưng không kém phần quan trọng, bạn cũng có thể sử dụng một cách đơn giản foreach:

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

1

Có lẽ là quá mức nếu bạn cần nó chỉ một lần, nhưng bạn có thể sử dụng thư viện YaLinqo * để lọc các bộ sưu tập (và thực hiện bất kỳ biến đổi nào khác). Thư viện này cho phép xác định các truy vấn giống như SQL trên các đối tượng với cú pháp lưu loát. whereHàm của nó chấp nhận một calback với hai đối số: một giá trị và một khóa. Ví dụ:

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

( whereHàm trả về một trình vòng lặp, vì vậy nếu bạn chỉ cần lặp lại với foreachchuỗi kết quả một lần, ->toArray()có thể được loại bỏ.)

* được phát triển bởi tôi


1

chức năng lọc mảng từ php:

array_filter ( $array, $callback_function, $flag )

$ mảng - Đây là mảng đầu vào

$ callback_function - Hàm gọi lại để sử dụng , Nếu hàm gọi lại trả về true , giá trị hiện tại từ mảng được trả về mảng kết quả.

$ flag - Đây là tham số tùy chọn , nó sẽ xác định đối số nào được gửi đến hàm gọi lại. Nếu tham số này trống thì hàm gọi lại sẽ lấy giá trị mảng làm đối số. Nếu bạn muốn gửi khóa mảng làm đối số thì hãy sử dụng cờ $ dưới dạng ARRAY_FILTER_USE_KEY . Nếu bạn muốn gửi cả khóa và giá trị, bạn nên sử dụng cờ $ dưới dạng ARRAY_FILTER_USE_BOTH .

Ví dụ: Xem xét mảng đơn giản

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

Nếu bạn muốn lọc mảng dựa trên khóa mảng , chúng ta cần sử dụng ARRAY_FILTER_USE_KEY làm tham số thứ ba của hàm mảng Array_filter.

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

Nếu bạn muốn lọc mảng dựa trên khóa mảng và giá trị mảng , Chúng ta cần sử dụng ARRAY_FILTER_USE_BOTH làm tham số thứ ba của hàm mảng Array_filter.

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

Các hàm gọi lại mẫu:

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

Nó sẽ xuất

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

0

Với chức năng này, bạn có thể lọc một mảng nhiều chiều

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

Đầu ra từ chạy


0

Giải pháp ngây thơ và xấu xí (nhưng dường như nhanh hơn)?

Chỉ thử điều này trong php 7.3.11 nhưng một vòng lặp xấu xí dường như thực thi trong khoảng một phần ba thời gian. Kết quả tương tự trên một mảng với vài trăm khóa. Tối ưu hóa vi mô, có thể không hữu ích trong RW, nhưng thấy nó đáng ngạc nhiên và thú vị:

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

Hàm loại bỏ một số phần tử mảng

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

gọi và in

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

kết quả Array ( [0] => first )


Câu hỏi là về việc lọc các khóa mảng không phải là giá trị.
Poletaew
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.