Cách tìm kiếm theo khóa => giá trị trong một mảng nhiều chiều trong PHP


147

Có cách nào nhanh chóng để có được tất cả các phân đoạn trong đó một cặp giá trị khóa được tìm thấy trong một mảng nhiều chiều không? Tôi không thể nói mảng sẽ sâu đến mức nào.

Mảng ví dụ đơn giản:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

Khi tôi tìm kiếm key = name và value = "cat 1", hàm sẽ trả về:

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

Tôi đoán chức năng phải được đệ quy để xuống mức sâu nhất.

Câu trả lời:


217

Mã số:

function search($array, $key, $value)
{
    $results = array();

    if (is_array($array)) {
        if (isset($array[$key]) && $array[$key] == $value) {
            $results[] = $array;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, search($subarray, $key, $value));
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1"));

print_r(search($arr, 'name', 'cat 1'));

Đầu ra:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => cat 1
        )

    [1] => Array
        (
            [id] => 3
            [name] => cat 1
        )

)

Nếu hiệu quả là quan trọng, bạn có thể viết nó để tất cả các cuộc gọi đệ quy lưu kết quả của chúng trong cùng một $resultsmảng tạm thời thay vì hợp nhất các mảng lại với nhau, như vậy:

function search($array, $key, $value)
{
    $results = array();
    search_r($array, $key, $value, $results);
    return $results;
}

function search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        search_r($subarray, $key, $value, $results);
    }
}

Chìa khóa ở đó là search_rlấy tham số thứ tư của nó bằng tham chiếu chứ không phải theo giá trị; ký hiệu &là rất quan trọng.

FYI: Nếu bạn có một phiên bản cũ của PHP thì bạn phải xác định phần pass-by-reference trong cuộc gọi đến search_rchứ không phải là trong tuyên bố của mình. Đó là, dòng cuối cùng trở thành search_r($subarray, $key, $value, &$results).


2
@JohnKugelman Sẽ $keykhông có lỗi trả lời "hiệu quả" nếu không tồn tại trong mảng? Nó sẽ tốt hơn để làm gì if (array_key_exists($key, $array) && $array[$key] == $value) {?
Đuổi theo

1
@JohnKugelman chức năng này hoạt động tốt đẹp, nhưng đôi khi tôi phải của tôi $valuemà là nullvà chức năng không làm việc ... array empty... Làm thế nào để có một mảng ngay cả khi $value= null? như thế search($array, 'id', null)nào
15:00

71

Làm thế nào về phiên bản SPL thay thế? Nó sẽ giúp bạn tiết kiệm một số thao tác gõ:

// I changed your input example to make it harder and
// to show it works at lower depths:

$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
             1 => array(array('id'=>3,'name'=>"cat 1")),
             2 => array('id'=>2,'name'=>"cat 2")
);

//here's the code:

    $arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));

 foreach ($arrIt as $sub) {
    $subArray = $arrIt->getSubIterator();
    if ($subArray['name'] === 'cat 1') {
        $outputArray[] = iterator_to_array($subArray);
    }
}

Điều tuyệt vời là về cơ bản cùng một mã sẽ lặp qua một thư mục cho bạn, bằng cách sử dụng RecursiveDirectoryIterator thay vì RecursiveArrayIterator. SPL là roxor.

Điểm mấu chốt duy nhất về SPL là nó được ghi lại rất nhiều trên web. Nhưng một số sách PHP đi vào một số chi tiết hữu ích, đặc biệt là Pro PHP; và bạn có thể có thể google để biết thêm thông tin.


Điều này hoạt động giống như một bùa mê và tôi dự định sử dụng nó một lần nữa cho các vấn đề tương tự: D Phần kỳ lạ duy nhất nằm trong foreach và sử dụng hàm getSubIterator trên RecursiveIteratorIterator thay vì biến $ sub. Tôi đã nghĩ rằng đó là một lỗi đánh máy lúc đầu nhưng đó là cách đúng đắn! cảm ơn Jared
bchhun

2
Giải pháp tuyệt vời. Khá nhanh quá!
TaylorOtwell

Cảm ơn bạn đã giải quyết. Chúng ta lấy "id" ở đâu? Từ $ outputArray?
trante

Cảm ơn, giải pháp rất thẳng về phía trước, nhưng không biết về hiệu suất ??.
Mahesh.D

Làm thế nào để bỏ đặt phần tử tìm thấy (có thể là một mảng con) từ mảng ban đầu?
Fr0zenFyr

49
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
             1 => array("id"=>2,"name"=>"cat 2"),
             2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
   return ($ar['name'] == 'cat 1');
   //return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});

echo "<pre>";
print_r($arr);

?>

Tham chiếu: http://php.net/manual/en/feft.array-filter.php


4
Đây là một giải pháp tốt nếu bạn muốn tìm kiếm một mảng chỉ sâu một cấp, nhưng câu hỏi cụ thể này là về việc tìm kiếm đệ quy thành một mảng sâu ("hàm phải được đệ quy để xuống cấp độ sâu nhất").
orrd

16

Trở lại để đăng bản cập nhật này cho bất cứ ai cần một mẹo tối ưu hóa cho những câu trả lời này, câu trả lời tuyệt vời của John Kugelman ở trên.

Chức năng được đăng của anh ấy hoạt động tốt nhưng tôi phải tối ưu hóa kịch bản này để xử lý một tập kết quả 12 000 hàng. Chức năng đã mất 8 giây vĩnh viễn để đi qua tất cả các bản ghi, waaaaaay quá lâu.

Tôi chỉ đơn giản là cần chức năng để DỪNG tìm kiếm và quay lại khi tìm thấy kết quả khớp. Tức là, nếu tìm kiếm một khách hàng_id, chúng tôi biết rằng chúng tôi chỉ có một trong tập kết quả và một khi chúng tôi tìm thấy khách hàng_id trong mảng nhiều chiều, chúng tôi muốn quay lại.

Đây là phiên bản được tối ưu hóa tốc độ (và đơn giản hóa nhiều) của chức năng này, cho bất kỳ ai có nhu cầu. Không giống như các phiên bản khác, nó chỉ có thể xử lý một độ sâu của mảng, không lặp lại và không kết hợp nhiều kết quả.

// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {   
    foreach ($array as $subarray){  
        if (isset($subarray[$key]) && $subarray[$key] == $value)
          return $subarray;       
    } 
}

Điều này mang lại nhiệm vụ phù hợp với 12 000 bản ghi trong 1,5 giây. Vẫn rất tốn kém nhưng hợp lý hơn nhiều.


câu hỏi này nhanh hơn câu trả lời của Jhon / Jared (0,0009999275207519) so với (0,0020008087158203) .. Vâng, bài kiểm tra này dành riêng cho trường hợp và môi trường của tôi .. Tôi sẽ kiên quyết với điều này, cảm ơn stefgosselin
Awena

14
if (isset($array[$key]) && $array[$key] == $value)

Một tầm quan trọng nhỏ cho phiên bản nhanh.


2
Trên thực tế, điều này ngăn nó ném cảnh báo khi khóa không được đặt. Không quá nhỏ! -> + 1'ed.
stefgosselin

2
đồng ý, có thể thực sự lướt qua nhật ký lỗi php cho các lỗi lớn và không bị ô nhiễm với các cảnh báo là cách để đi theo ý kiến ​​của tôi.
tiền mã hóa

Đây không phải là một giải pháp hoàn chỉnh và nhiều hơn là "Cố gắng phản hồi bài đăng khác" và "Không trả lời".
mickmackusa

7

Cẩn thận với các thuật toán tìm kiếm tuyến tính (ở trên là tuyến tính) trong các mảng nhiều chiều vì chúng có độ phức tạp phức tạp vì độ sâu của nó làm tăng số lần lặp cần thiết để đi qua toàn bộ mảng. Ví dụ:

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)

sẽ mất tối đa 200 lần lặp để tìm thấy những gì bạn đang tìm kiếm (nếu kim ở mức [100] [1]), với một thuật toán phù hợp.

Các thuật toán tuyến tính trong trường hợp này thực hiện ở O (n) (tổng số phần tử trong toàn bộ mảng), điều này rất kém, một triệu mục (ví dụ: mảng 1000x100x10) sẽ mất trung bình 500.000 lần lặp để tìm kim. Ngoài ra điều gì sẽ xảy ra nếu bạn quyết định thay đổi cấu trúc của mảng đa chiều của bạn? Và PHP sẽ khởi động một thuật toán đệ quy nếu độ sâu của bạn là hơn 100. Khoa học máy tính có thể làm tốt hơn:

Nếu có thể, luôn luôn sử dụng các đối tượng thay vì mảng nhiều chiều:

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)

và áp dụng một giao diện và chức năng so sánh tùy chỉnh để sắp xếp và tìm thấy chúng:

interface Comparable {
   public function compareTo(Comparable $o);
}

class MyObject implements Comparable {
   public function compareTo(Comparable $o){
      ...
   }
}

function myComp(Comparable $a, Comparable $b){
    return $a->compareTo($b);
}

Bạn có thể sử dụng uasort()để sử dụng một bộ so sánh tùy chỉnh, nếu bạn cảm thấy thích phiêu lưu, bạn nên triển khai các bộ sưu tập của riêng mình cho các đối tượng có thể sắp xếp và quản lý chúng (tôi luôn luôn mở rộng ArrayObject để bao gồm ít nhất một chức năng tìm kiếm).

$arrayObj->uasort("myComp");

Khi chúng được sắp xếp (uasort là O (n log n), điều này tốt như nó vượt qua dữ liệu tùy ý), tìm kiếm nhị phân có thể thực hiện thao tác trong thời gian O (log n), tức là một triệu mục chỉ mất ~ 20 lần lặp Tìm kiếm. Theo như tôi biết, tìm kiếm nhị phân so sánh tùy chỉnh không được triển khai trong PHP ( array_search()sử dụng thứ tự tự nhiên hoạt động trên các tham chiếu đối tượng không phải là thuộc tính của chúng), bạn sẽ phải tự thực hiện điều này giống như tôi.

Cách tiếp cận này hiệu quả hơn (không còn độ sâu) và quan trọng hơn là phổ quát (giả sử bạn thực thi khả năng so sánh bằng giao diện) vì các đối tượng xác định cách chúng được sắp xếp, do đó bạn có thể tái chế mã vô hạn. Tốt hơn nhiều =)


Câu trả lời này phải chính xác. Mặc dù phương pháp tìm kiếm vũ phu sẽ làm điều đó, nhưng điều này ít tốn nhiều tài nguyên hơn.
vẽ

Cần lưu ý rằng những gì bạn đề xuất chỉ có ý nghĩa nếu bạn đang tìm kiếm cùng một mảng nhiều lần. Phải mất nhiều thời gian hơn để trải qua những rắc rối của việc sắp xếp nó (O (n log n)) so với việc tìm kiếm tuyến tính cho giá trị (O (n)). Nhưng một khi nó được sắp xếp, chắc chắn, sau đó tìm kiếm nhị phân sẽ nhanh hơn.
orrd

Tôi cũng nên thêm rằng sử dụng các đối tượng thay vì mảng có thể là một sự trừu tượng hữu ích, nhưng bạn cũng có thể thực hiện tìm kiếm nhị phân trên một mảng nếu mảng được sắp xếp. Bạn không cần sử dụng các đối tượng để sắp xếp một mảng hoặc thực hiện tìm kiếm nhị phân trên đó.
orrd

6

Đây là giải pháp:

<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");

$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;  

?>

5
$result = array_filter($arr, function ($var) {   
  $found = false;
  array_walk_recursive($var, function ($item, $key) use (&$found) {  
    $found = $found || $key == "name" && $item == "cat 1";
  });
  return $found;
});

3

http://snipplr.com/view/51108/nested-array-search-by-value-or-key/

<?php

//PHP 5.3

function searchNestedArray(array $array, $search, $mode = 'value') {

    foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
        if ($search === ${${"mode"}})
            return true;
    }
    return false;
}

$data = array(
    array('abc', 'ddd'),
    'ccc',
    'bbb',
    array('aaa', array('yyy', 'mp' => 555))
);

var_dump(searchNestedArray($data, 555));

3
function in_multi_array($needle, $key, $haystack) 
{
    $in_multi_array = false;
    if (in_array($needle, $haystack))
    {
        $in_multi_array = true; 
    }else 
    {
       foreach( $haystack as $key1 => $val )
       {
           if(is_array($val)) 
           {
               if($this->in_multi_array($needle, $key, $val)) 
               {
                   $in_multi_array = true;
                   break;
               }
           }
        }
    }

    return $in_multi_array;
} 

trường hợp của tôi là khác nhau nhưng có gợi ý từ câu trả lời của bạn.
shyammakwana.me

2

Tôi cần một cái gì đó tương tự, nhưng để tìm kiếm mảng đa chiều theo giá trị ... Tôi lấy ví dụ của John và viết

function _search_array_by_value($array, $value) {
        $results = array();
        if (is_array($array)) {
            $found = array_search($value,$array);
            if ($found) {
                $results[] = $found;
            }
            foreach ($array as $subarray)
                $results = array_merge($results, $this->_search_array_by_value($subarray, $value));
        }
        return $results;
    }

Tôi hy vọng nó sẽ giúp được ai đó :)


2

Đây là một chức năng được sửa đổi từ một chức năng mà John K. đã đăng ... Tôi chỉ cần lấy khóa cụ thể trong mảng và không có gì ở trên nó.

function search_array ( $array, $key, $value )
{
    $results = array();

    if ( is_array($array) )
    {
        if ( $array[$key] == $value )
        {
            $results[] = $array;
        } else {
            foreach ($array as $subarray) 
                $results = array_merge( $results, $this->search_array($subarray, $key, $value) );
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
       1 => array(id=>2,name=>"cat 2"),
       2 => array(id=>3,name=>"cat 1"));

print_r(search_array($arr, 'name', 'cat 1'));

1

Và một phiên bản khác trả về giá trị khóa từ phần tử mảng trong đó giá trị được tìm thấy (không có đệ quy, được tối ưu hóa cho tốc độ):

// if the array is 
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);

//then 
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) ) 
// instead of Array ( [0] => Array ( [id] => 2 ) )

// search array for specific key = value
function search_array($array, $key, $value) {
  $return = array();   
  foreach ($array as $k=>$subarray){  
    if (isset($subarray[$key]) && $subarray[$key] == $value) {
      $return[$k] = $subarray;
      return $return;
    } 
  }
}

Cảm ơn tất cả những người đã đăng ở đây.


1
function findKey($tab, $key){
    foreach($tab as $k => $value){ 
        if($k==$key) return $value; 
        if(is_array($value)){ 
            $find = findKey($value, $key);
            if($find) return $find;
        }
    }
    return null;
}

2
Bạn có thể mở rộng về câu trả lời này? Mã chỉ trả lời không giải thích những gì bạn đang thực sự làm.
Rich Benner

Hãy cập nhật câu hỏi của bạn với mục đích giáo dục.
mickmackusa

Đây là chức năng chỉ tìm Key, Điều này làm việc cho tôi.
Giovanny Gonzalez

0

Nếu bạn muốn tìm kiếm các phím này thì tốt

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            $results[] = $resultArray;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

Các khóa sẽ không ghi đè vì mỗi bộ khóa => giá trị sẽ nằm trong mảng riêng trong mảng kết quả.
Nếu bạn không muốn khóa trùng lặp thì hãy sử dụng khóa này

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            foreach($resultArray as $key => $single) {

                $results[$key] = $single;
            }
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}
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.