需在二維或多維陣列中判斷某值是否存在,PHP 內建 in_array() 僅支援一維陣列,需自行實作遞歸或迭代遍歷。
function search_2d($keyword, array $stack): bool
{
foreach ($stack as $subArray) {
if (in_array($keyword, $subArray, true)) { // strict 模式建議開啟
return true;
}
}
return false;
}
// 使用範例
$data = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
];
var_dump(search_2d('Alice', $data)); // true
var_dump(search_2d('Charlie', $data)); // false
適用場景:固定二維結構、效能優先、不需知道具體鍵名路徑。
/**
* 遞歸搜尋多維陣列,回傳所有包含目標值的鍵路徑
*
* @param array $array 目標陣列
* @param mixed $value 搜尋值
* @param array $path 內部遞歸用:當前路徑
* @return array 扁平化的路徑陣列,每項為 ['path' => [...], 'value' => mixed]
*/
function search_recursive(array $array, $value, array $path = []): array
{
$results = [];
foreach ($array as $key => $val) {
$currentPath = array_merge($path, [$key]);
if (is_array($val)) {
// 遞歸進入子陣列
$results = array_merge($results, search_recursive($val, $value, $currentPath));
} elseif ($val === $value) { // 嚴格比較
$results[] = ['path' => $currentPath, 'value' => $val];
}
}
return $results;
}
// 使用範例
$data = [
'users' => [
['id' => 1, 'tags' => ['admin', 'vip']],
['id' => 2, 'tags' => ['editor']],
],
'settings' => ['theme' => 'dark', 'lang' => 'zh-TW'],
];
$matches = search_recursive($data, 'vip');
print_r($matches);
/*
Array
(
[0] => Array
(
[path] => Array ( [0] => users [1] => 0 [2] => tags [3] => 1 )
[value] => vip
)
)
*/
class RecursiveArraySearch implements Iterator
{
private $iterator;
private $target;
private $currentMatch;
public function __construct(array $array, $target)
{
$this->iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::LEAVES_ONLY
);
$this->target = $target;
}
public function rewind() { $this->iterator->rewind(); $this->findNext(); }
public function valid() { return $this->currentMatch !== null; }
public function current() { return $this->currentMatch; }
public function key() { return $this->iterator->getSubIterator()->key(); }
public function next() { $this->iterator->next(); $this->findNext(); }
private function findNext()
{
$this->currentMatch = null;
foreach ($this->iterator as $key => $val) {
if ($val === $this->target) {
// 建構完整路徑
$path = [];
for ($depth = $this->iterator->getDepth(); $depth >= 0; $depth--) {
$path[] = $this->iterator->getSubIterator($depth)->key();
}
$this->currentMatch = ['path' => array_reverse($path), 'value' => $val];
break;
}
}
}
}
// 使用:foreach 逐個取得匹配,不一次載入所有結果
foreach (new RecursiveArraySearch($hugeArray, 'target') as $match) {
echo "Found at: " . implode(' → ', $match['path']) . "\n";
// 可隨時 break 停止搜尋
}
| 方案 | 時間複雜度 | 空間複雜度 | 優點 | 缺點 |
|---|---|---|---|---|
| 二維迭代 | O(n×m) | O(1) | 簡單、快速、無遞歸風險 | 僅限二維 |
| 遞歸回傳陣列 | O(N) | O(D + R) | 取得完整路徑、程式碼直觀 | 深度過大可能堆疊溢位、一次產生所有結果佔記憶體 |
| RecursiveIteratorIterator | O(N) | O(D) | 惰性求值、記憶體恆定、支援中斷 | 程式碼較複雜、路徑建構需手動 |
N = 總元素數,D = 最大深度,R = 匹配結果數。
in_array() 及 == 會將 "1" 與 1 視為相同,建議一律使用 === 或 in_array($v, $arr, true)。$a[] = &$a),遞歸會無限迴圈。需維護 $visited = [] 記錄 spl_object_id() 或序列化指紋。整理自 heicm 技術筆記,補充遞歸路徑回傳與迭代器模式實作