如何在 PHP 中的多维数组中按 key=>value 进行搜索

Posted

技术标签:

【中文标题】如何在 PHP 中的多维数组中按 key=>value 进行搜索【英文标题】:How to search by key=>value in a multidimensional array in PHP 【发布时间】:2010-11-04 09:08:15 【问题描述】:

是否有任何快速的方法来获取在多维数组中找到键值对的所有子数组?我不能说阵列会有多深。

简单示例数组:

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

我猜这个函数必须是递归的才能达到最深层次。

【问题讨论】:

【参考方案1】:

代码:

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,那么您必须在 call 中指定传递引用部分到search_r,而不是在其声明中。也就是最后一行变成search_r($subarray, $key, $value, &$results)

【讨论】:

【参考方案2】:

改用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 是 roxor。

关于 SPL 的唯一遗憾是它在网络上的记录很糟糕。但是有几本 PHP 书籍介绍了一些有用的细节,尤其是 Pro PHP;你也可以通过谷歌搜索更多信息。

【讨论】:

这就像一个魅力,我计划再次使用它来解决类似的问题:D 唯一奇怪的部分是在 foreach 中并在 RecursiveIteratorIterator 上使用 getSubIterator 函数而不是 $sub 变量。起初我以为是错字,但这是正确的方法!谢谢贾里德。 感谢您的解决方案。我们从哪里得到“id”?来自 $outputArray? 谢谢,非常直接的解决方案,但不了解性能??。 如何从原始数组中取消设置找到的元素(可能是子数组)?【参考方案3】:

我需要类似的东西,但是要按值搜索多维数组...我以 John 为例并写道

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;
    

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

【讨论】:

【参考方案4】:
if (isset($array[$key]) && $array[$key] == $value)

对快速版本的小幅改进。

【讨论】:

实际上这可以防止它在未设置密钥时抛出警告。没那么小! -> +1'ed。 同意,在我看来,能够真正浏览 php 错误日志中的重大错误并且不会被警告污染是要走的路。 这不是一个完整的解决方案,因此更像是“尝试回复另一个帖子”和“不是答案”。【参考方案5】:

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

【讨论】:

【参考方案6】:
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;
 

【讨论】:

我的情况不同,但从您的回答中得到了提示。【参考方案7】:

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

他发布的函数工作正常,但我必须优化此场景以处理 12 000 行结果集。该函数需要 8 秒才能遍历所有记录,太长了。

我只需要该函数停止搜索并在找到匹配项时返回。即,如果搜索 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/Jared 的答案 (0.0009999275207519) vs (0.0020008087158203) 快。这个测试是针对我的案例和环境的。我坚持这个,谢谢 stefgosselin【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:
$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;
);

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

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() 使用自然排序,它适用于对象引用而不是它们的属性),你必须像我一样自己实现这个。

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

【讨论】:

这个答案应该是正确的。虽然蛮力搜索方法可以做到这一点,但它占用的资源要少得多。 应该注意的是,你的建议只有在你多次搜索同一个数组时才有意义。与简单地对值进行线性搜索 (O(n)) 相比,对它进行排序 (O(n log n)) 所花费的时间要长得多。但是一旦它被排序,当然,然后二进制搜索会更快。 我还应该补充一点,使用对象而不是数组可能是一种有用的抽象,但如果数组已排序,您也可以对数组进行二进制搜索。您无需使用对象对数组进行排序或对其进行二分搜索。 如果有人使用正确实现的多线程,它只需要对 needle @ [100][1] 进行一次操作。从 [0][0] 向前、从 [100][1] 向后、从 [50][1] 向后和从 [50][0] 向前启动一个线程。只要您的处理器不会崩溃,您基本上可以通过添加更多线程来减少搜索时间^^【参考方案12】:
<?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

【讨论】:

如果您想搜索只有一层深度的数组,这是一个很好的解决方案,但这个特殊问题是关于递归搜索深度数组(“函数必须递归才能下到最深层次”)。【参考方案13】:

这里是解决方案:

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

?>

【讨论】:

【参考方案14】:
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;

【讨论】:

你能扩展这个答案吗?仅代码答案并不能解释您实际在做什么。 请更新您的问题以进行教育。 这仅适用于查找密钥,这对我有用。【参考方案15】:

如果你想搜索键数组,这很好

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;

【讨论】:

【参考方案16】:

如果你知道你的密钥,我认为最简单的方法是使用 php 数组函数。

function search_array ( $array, $key, $value )

   return array_search($value,array_column($array,$key));

这会返回一个索引,您可以通过以下方式找到所需的数据:

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

echo json_encode($arr[search_array($arr,'name','cat 2')]);

此输出将:

"id":2,"name":"cat 2"

【讨论】:

【参考方案17】:

2 个函数:array_search_key_value 返回键数组以到达具有多维数组中的值的键,array_extract_keys 返回键数组指向的多维数组中的值。

function array_search_key_value($array, $key, $value) 
    if (!is_array($array)) 
        return false;
    

    return array_search_key_value_aux($array, $key, $value);


function array_search_key_value_aux($array, $key, $value, $path=null) 
    if (array_key_exists($key, $array) && $array[$key] === $value) 
        $path[]=$key;

        return $path;
    

    foreach ($array as $k => $v ) 
        if (is_array($v)) 
            $path[]=$k;

            $p = array_search_key_value_aux($v, $key, $value, $path);

            if ($p !== false) 
                return $p;
            
        
    

    return false;


function array_extract_keys($array, $key_list) 
    $v = $array;

    foreach ($key_list as $key) 
        if (!is_array($v) || !array_key_exists($key, $v))
            return false;

        $v = &$v[$key];
     
       
    return $v;

这是一个单一的测试:

$test_array = array(
    'a' => array(
        'aa' => true,
        'ab' => array(
            'aaa' => array(
                'one' => 1,
                'two' => 2,
                'three' => 3,
                'four' => 4
            ),
            'four' => 4,
            'five' => 5,
        ),
        'six' => 6,
    ),
    'seven' => 7
);

$test_data = array(
    array('one', 1),
    array('two', 2),
    array('three', 3),
    array('four', 4),
    array('five', 5),
    array('six', 6),
    array('seven', 7),
    array('zero', 0),
    array('one', 0),
);

foreach ($test_data as $d) 
    $r = array_search_key_value($test_array, $d[0], $d[1]);

    echo $d[0] . ' => ' . $d[1] . ' ? ', $r ? implode('/', $r) . ' => ' . array_extract_keys($test_array, $r) : 'null', PHP_EOL;

【讨论】:

以上是关于如何在 PHP 中的多维数组中按 key=>value 进行搜索的主要内容,如果未能解决你的问题,请参考以下文章

PHP 多维数组 Key Value的使用

如何从 PHP 中的多维数组中删除重复值

PHP array_key_exists 不起作用;数组不是多维的

如何在 C# 中按升序对多维数组进行排序? [复制]

php array_key_exists 的多维数组版本

如何从 PHP 中的多维数组中删除重复值 [重复]