如何在PHP中通过多维数组中的key => value搜索


147

有没有一种快速的方法来获取在多维数组中找到键值对的所有子数组?我不能说阵列有多深。

简单示例数组:

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

当我搜索key = name和value =“ cat 1”时,该函数应返回:

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

我猜想函数必须递归才能深入到最深层次。

Answers:


217

码:

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

输出:

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

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

)

如果效率很重要,则可以编写效率代码,以便所有递归调用将其结果存储在同一临时$results数组中,而不是将数组合并在一起,如下所示:

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

这里的关键是search_r通过引用而不是值来获取其第四个参数。“&”号&至关重要。

仅供参考:如果您使用的是旧版PHP,则必须在的调用search_r而不是在其声明中指定引用传递部分。也就是说,最后一行变为search_r($subarray, $key, $value, &$results)


2
@JohnKugelman如果$key数组中不存在错误的“高效”答案错误 ?会更好if (array_key_exists($key, $array) && $array[$key] == $value) {吗?
Chase

1
@JohnKugelman此功能不错,但有时我有我$value这是null和功能不工作... array empty...如何有一个数组即使$value= null?喜欢search($array, 'id', null)吗?
Zagloo 2015年

71

如何在SPL版本呢?它将节省您一些键入操作:

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

很棒的是,通过使用RecursiveDirectoryIterator而不是RecursiveArrayIterator,基本上相同的代码将为您遍历目录。SPL是近似值。

关于SPL的唯一遗憾是,它在网络上的记录很差。但是有几本PHP书中有一些有用的细节,特别是Pro PHP。您也可以在Google上搜索更多信息。


这就像一种魅力,我计划再次将其用于类似的问题:D唯一奇怪的部分是在foreach中,并在RecursiveIteratorIterator上使用getSubIterator函数而不是$ sub变量。我起初以为是错字,但这是正确的方法!谢谢贾里德。
bchhun 2011年

2
很棒的解决方案。太快了!
TaylorOtwell 2011年

谢谢您的解决方案。我们从哪里获得“身份证”?从$ outputArray?
trante 2012年

谢谢,非常直接的解决方案,但不了解性能??。
Mahesh.D

如何从原始数组中取消找到的元素(可能是子数组)?
Fr0zenFyr 2015年

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

?>

参考:http : //php.net/manual/en/function.array-filter.php


4
如果要搜索仅深一层的数组,这是一个很好的解决方案,但是这个特定的问题是关于递归搜索到深层数组(“函数必须递归到最深的级别”)。
2015年

16

回来向需要这些答案的优化提示的任何人发布此更新,特别是上面提到的John Kugelman的出色答案。

他发布的函数工作正常,但我不得不优化此方案以处理12000行结果集。该功能要花费8秒钟才能完成所有记录,所以时间太长了。

我只需要该函数停止搜索并在找到匹配项时返回即可。也就是说,如果要搜索一个customer_id,我们知道结果集中只有一个,并且一旦在多维数组中找到了customer_id,我们就想返回。

这是此功能的速度优化(且经过简化)的版本,适合需要的任何人。与其他版本不同,它只能处理一个深度的数组,不能递归,并且不能合并多个结果。

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

这样就完成了将12000条记录与1.5秒匹配的任务。仍然非常昂贵,但更加合理。


这个人是不是JHON更快/贾里德的答案(0.0009999275207519)VS(0.0020008087158203)..那么这个测试是针对我的情况和环境..即时通讯这种坚持,感谢stefgosselin
Awena

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

快速版本的次要改进。


2
实际上,这可以防止在未设置键时引发警告。不那么小!-> +1。
stefgosselin 2011年

2
我同意,能够实际浏览php错误日志中的主要错误并且不使其受到警告污染是我认为的方法。
codercake 2011年

这不是一个完整的解决方案,因此更多的是“尝试回复另一个帖子”和“没有答案”。
mickmackusa

7

请注意多维数组中的线性搜索算法(以上为线性),因为它们的深度会增加遍历整个数组所需的迭代次数,因此复杂度更高。例如:

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

使用合适的算法,最多需要200次迭代才能找到您要寻找的内容(如果针头位于[100] [1])。

在这种情况下,线性算法以O(n)(整个数组中元素的有序总数)执行,这很差,一百万个条目(例如1000x100x10数组)平均需要500,000次迭代才能找到针。如果您决定更改多维数组的结构,还会发生什么?如果深度超过100,PHP将推出一种递归算法。计算机科学可以做得更好:

如果可能,请始终使用对象而不是多维数组:

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

并应用自定义比较器接口和函数进行排序和查找:

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

您可以使用uasort()自定义比较器,如果您喜欢冒险的话,应该为可以对它们进行排序和管理的对象实现自己的集合(我总是将ArrayObject至少扩展为包括搜索功能)。

$arrayObj->uasort("myComp");

一旦对它们进行了排序(uasort是O(n log n),它可以处理任意数据一样好),二进制搜索就可以在O(log n)时间内完成操作,即一百万个条目只需要花费约20次迭代即可搜索。据我所知,自定义比较器二进制搜索未在PHP中实现(array_search()使用对对象引用而非其属性起作用的自然顺序),您将必须像我一样自行实现。

这种方法更有效(不再有深度),更重要的是通用(假设您使用接口强制可比性),因为对象定义了它们的排序方式,因此您可以无限地回收代码。更好=)


这个答案应该是正确的。尽管可以使用蛮力搜索方法进行,但这要消耗更少的资源。
2013年

应该注意的是,只有多次搜索同一阵列时,您的建议才有意义。与仅对值(O(n))进行线性搜索相比,进行排序(O(n log n))所需的时间要长得多。但可以肯定的是,一旦将其排序,二进制搜索就会更快。
2015年

我还应该补充一点,使用对象而不是数组可能是有用的抽象方法,但是如果对数组排序,也可以对数组进行二进制搜索。您不需要使用对象对数组进行排序或对其进行二进制搜索。
2015年

6

这是解决方案:

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

我的情况不同,但从您的答案中得到了提示。
shyammakwana.me,2015年

2

我需要类似的东西,但要通过值搜索多维数组...我以约翰为例,并写道

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

我希望它可以帮助某人:)


2

这是John K.发布的功能的修订功能。我只需要获取数组中的特定键,而无需获取上方的任何内容。

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

另一个版本从找到该值的数组元素中返回键值(无递归,针对速度进行了优化):

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

感谢所有在这里发布的人。


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
您能否扩展这个答案?仅代码答案无法解释您的实际工作。
Rich Benner

请更新您的问题以进行教育。
mickmackusa

这仅适用于查找密钥,对我有用。
Giovanny Gonzalez

0

如果要搜索键数组,这很好

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

键不会覆盖,因为每组键=>值将位于结果数组中的单独数组中。
如果您不希望重复的键,请使用此键

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.