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

143 php arrays recursion search

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

简单示例数组:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);
Run Code Online (Sandbox Code Playgroud)

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

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);
Run Code Online (Sandbox Code Playgroud)

我想这个函数必须递归才能达到最深层次.

Joh*_*ica 203

码:

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'));
Run Code Online (Sandbox Code Playgroud)

输出:

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

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

)
Run Code Online (Sandbox Code Playgroud)

如果效率很重要,您可以编写它,以便所有递归调用将其结果存储在同一个临时$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);
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是search_r通过引用而不是值来获取第四个参数; &符号&至关重要.

FYI:如果你有PHP的是旧版本,那么你必须在指定通通过基准部电话search_r,而不是在其声明.也就是说,最后一行变为search_r($subarray, $key, $value, &$results).

  • @JohnKugelman如果数组中不存在`$ key`,那么"高效"答案会出错吗?做if(array_key_exists($ key,$ array)&& $ array [$ key] == $ value){`? (2认同)

jar*_*red 70

相反,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最棒的是,基本相同的代码将通过使用RecursiveDirectoryIterator而不是RecursiveArrayIterator来遍历目录.SPL是roxor.

关于SPL的唯一令人遗憾的是它在网络上的记录很糟糕.但是有几本PHP书籍会介绍一些有用的细节,特别是Pro PHP; 你可以谷歌获取更多信息.

  • 很棒的解决方案.太快了! (2认同)

Pra*_*dra 42

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

?>
Run Code Online (Sandbox Code Playgroud)

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

  • 如果你想搜索一个只有一层深度的数组,这是一个很好的解决方案,但是这个特殊的问题是关于递归搜索深度数组("函数必须递归以达到最深层次"). (4认同)

ste*_*lin 16

回过头来发布这个更新给任何需要优化提示的人,特别是John Kugelman上面的回答.

他发布的函数工作正常,但我不得不优化这个场景来处理12 000行结果集.这个功能需要花费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;       
    } 
}
Run Code Online (Sandbox Code Playgroud)

这降低了将12 000条记录与1.5秒相匹配的任务.仍然非常昂贵,但更合理.


小智 14

if (isset($array[$key]) && $array[$key] == $value)
Run Code Online (Sandbox Code Playgroud)

对快速版本的一个小修改.

  • 实际上这可以防止它在未设置密钥时抛出警告.不是那么轻微! - > + 1'ed. (2认同)
  • 同意,能够实际浏览php错误日志中的重大错误并且没有被警告污染是我认为的方式. (2认同)

小智 7

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

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)
Run Code Online (Sandbox Code Playgroud)

将使用合适的算法,在最多200次迭代中找到您要查找的内容(如果针位于[100] [1]).

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

在可能的情况下,始终使用对象而不是多维数组:

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)
Run Code Online (Sandbox Code Playgroud)

并应用自定义比较器接口和函数来排序和查找它们:

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);
}
Run Code Online (Sandbox Code Playgroud)

你可以使用uasort()自定义比较器,如果你有冒险精神,你应该为你的对象实现自己的集合,可以对它们进行排序和管理(我总是扩展ArrayObject以至少包含一个搜索功能).

$arrayObj->uasort("myComp");
Run Code Online (Sandbox Code Playgroud)

一旦它们被排序(uasort是O(n log n),它与任意数据一样好),二进制搜索可以在O(log n)时间内进行操作,即一百万个条目只需要大约20次迭代搜索.据我所知,自定义比较器二进制搜索没有在PHP中实现(array_search()使用自然顺序,它适用于对象引用而不是它们的属性),你必须像我一样实现这一点.

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


小智 7

这是解决方案:

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

?>
Run Code Online (Sandbox Code Playgroud)


Vit*_*nko 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;
});
Run Code Online (Sandbox Code Playgroud)