[英]Search in hierarchical data in PHP
这是我拥有的数据结构(为了更清晰地理解而对其进行了简化):
• USA
• Alabama
• Montgomery
• Birmingham
• Arizona
• Phoenix
• Mesa
• Gilbert
• Germany
• West Germany
• Bonn
• Cologne
我需要返回给定节点的所有路径-即:如果用户输入Arizona
,则需要返回USA → Arizona
。 如果输入Birmingham
,我需要返回USA → Alabama → Birmingham
。
在PHP中是否有在这种结构中进行搜索的简单方法?
如果您没有巨大的数据结构,则可以使用XML解析。 它是众所周知的且易于实现。 它具有访问父元素的所需功能。
这是一个简单的例子:
$xml = <<<XML
<list>
<state name="USA">
<region name="Alabama">
<city name="Montgomery" />
<city name="Birmingham" />
</region>
<region name="Arizona">
<city name="Phoenix" />
<city name="Mesa" />
<city name="Gilbert" />
</region>
</state>
<state name="Germany">
<region name="West Germany">
<city name="Bonn" />
<city name="Cologne" />
</region>
</state>
</list>
XML;
$doc = new \DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->loadXML($xml);
$xpath = new \DOMXPath($doc);
// XPath query to match all elements with
// attribute name equals to your searched phrase
$locations = $xpath->query("//*[@name='Cologne']");
function parse($list) {
$response = [];
foreach ($list as $node) {
$response[] = $node->attributes->getNamedItem('name')->nodeValue;
$parentNode = $node->parentNode;
// traverse up to root element
// root element has no attributes
// feel free to use any other condition, such as checking to element's name
while ($parentNode->hasAttributes()) {
$response[] = $parentNode->attributes->getNamedItem('name')->nodeValue;
$parentNode = $parentNode->parentNode;
}
}
return $response;
}
$parsedLocations = array_reverse(parse($locations));
echo implode(' → ', $parsedLocations), PHP_EOL;
这是一种逐步构建路径的可能策略:从数组的第一级开始,然后检查searc项是否等于键。 如果不是,则检查该值,否则,如果该值是一个数组( is_array() ),则使用前缀递归重复搜索。
数据集:
$str = array(
"USA" => array(
"Alabama" => array(
"Montgomery",
"Birmingham"
),
"Arizona" => array(
"Phoenix",
"",
"Gilbert"
),
"West Germany" => array(
"Bonn",
"",
"Cologne"
)
),
"Germany" => array(
"West Germany" => array(
"Bonn",
"Mesa",
"Cologne"
)
)
);
功能:
function getPath($haystack, $needle, $prefix=""){
$path = "";
foreach($haystack as $key=>$value){
if($path!="")break;
if($key===$needle){
return $prefix.$key;
break;
}
elseif($value===$needle) {
return $prefix.$value;
break;
}
elseif(is_array($value)) {
$path.=getPath($value,$needle,$prefix.$key."=>");
}
}
return $path;
}
一个测试:
echo getPath($str,"Mesa");
如果重复,您将获得第一个结果。 如果未找到搜索词,则会得到一个空字符串。
由于“数据结构”非常模糊,并且您唯一的提示是您正在使用PHP,因此我假设您的“数据结构”含义如下:
[
'USA' =>
[
'Alabama' =>
[
'Montgomery',
'Birmingham'
],
'Arizona' =>
[
'Phoenix',
'Mesa',
'Gilbert'
]
],
'Germany' =>
[
'West Germany' =>
[
'Bonn',
'Cologne'
]
]
]
我假设您想要表格中的结果
['USA', 'Alabama', 'Birmingham']
如果不是这种情况,请告知我们您的数据实际上是如何可用的以及如何获得结果。
在PHP中是否有在这种结构中进行搜索的简单方法?
那取决于您对“简单”的定义。
对我来说,适合单个功能的解决方案是“简单的”。
但是,没有一种可立即使用的开箱即用的解决方案。
如果只需要查找“叶子”,则可以像在此StackOverflow问题中那样,在RecursiveIteratorIterator
上使用RecursiveArrayIterator
。
但是,由于您还需要找到中间密钥,因此它并不是真正的选择。
array_walk_recursive
。
您可能使用了ArrayIterator
或array_walk
,但是在此示例中,除了使事情复杂之外,它们实际上无法执行foreach
循环无法执行的任何操作。
因此,我将使用一个foreach
循环:
function findMyThing($needle, $haystack) // Keep argument order from PHP array functions
{
// We need to set up a stack array + a while loop to avoid recursive functions for those are evil.
// Recursive functions would also complicate things further in regard of returning.
$stack =
[
[
'prefix' => [],
'value' => $haystack
]
];
// As long as there's still something there, don't stop
while(count($stack) > 0)
{
// Copy the current stack and create a new, empty one
$currentStack = $stack;
$stack = [];
// Work the stack
for($i = 0; $i < count($currentStack); $i++)
{
// Iterate over the actual array
foreach($currentStack[$i]['value'] as $key => $value)
{
// If the value is an array, then
// 1. the key is a string (so we need to match against it)
// 2. we might have to go deeper
if(is_array($value))
{
// We need to build the current prefix list regardless of what we're gonna do below
$prefix = $currentStack[$i]['prefix'];
$prefix[] = $key;
// If the current key, is the one we're looking for, heureka!
if($key == $needle)
{
return $prefix;
}
// Otherwise, push prefix & value onto the stack for the next loop to pick up
else
{
$stack[] =
[
'prefix' => $prefix,
'value' => $value
];
}
}
// If the value is NOT an array, then
// 1. the key is an integer, so we DO NOT want to match against it
// 2. we need to match against the value itself
elseif($value == $needle)
{
// This time append $value, not $key
$prefix = $currentStack[$i]['prefix'];
$prefix[] = $value;
return $prefix;
}
}
}
}
// At this point we searched the entire array and didn't find anything, so we return an empty array
return [];
}
然后像这样使用它
$path = findMyThing('Alabama', $array);
@Siguza
避免使用递归函数,因为它们是邪恶的
递归不是邪恶的(或评估),并且与堆栈
function df($v,array &$in,array &$stack,$search) {
$stack[] = $v;
if ( $v == $search ) {
return [true,$stack];
}
if ( is_array($in) ) {
foreach ($in as $vv => $k) {
if ( is_array($k) ) {
$r = df($vv, $k, $stack, $search);
if ($r[0]) {
return $r;
}
}
else if ($k == $search) {
$stack[] = $k;
return [true,$stack];
}
}
}
array_pop($stack);
return [false,null];
}
用法:
$s = [];
$r = df('',$in,$s,'Bonn');
print_r($r);
$s = [];
$r = df('',$in,$s,'West Germany');
print_r($r);
$s = [];
$r = df('',$in,$s,'NtFound');
print_r($r);
输出:
Array
(
[0] => 1
[1] => Array
(
[0] =>
[1] => Germany
[2] => West Germany
[3] => Bonn
)
)
Array
(
[0] => 1
[1] => Array
(
[0] =>
[1] => Germany
[2] => West Germany
)
)
Array
(
[0] =>
[1] =>
)
根据您的数据结构。
$data['USA'] = ['Alabama' => ['Montgomery','Birmingham'],'Arizona' => ['Phoenix','Mesa','Gilbert']];
$data['Germany'] = ['West Germany' => ['Bonn','Cologne']];
function getHierarchy($location, $data){
$totalCountries = count($data);
//Get Array Keys of rows eg countries.
$keys = array_keys($data);
$hierarchy= [];
//Loop Through Countries
for($i = 0; $i < $totalCountries; $i++){
//If we have found the country then return it.
if($location == $keys[$i]) return [$keys[$i]];
$hierarchy[] = $keys[$i];
foreach($data[$keys[$i]] as $city => $places){
// if we have found the city then return it with country.
if($city == $location){
$hierarchy[] = $city;
return $hierarchy;
}
// if we have found the place in our places array then return it with country -> city -> place.
if(in_array($location, $places)){
$hierarchy[] = $city;
$hierarchy[] = $location;
return $hierarchy;
}
}
// Reset Hirarcy if we do not found our location in previous country.
$hierarchy = [];
}
}
$found = getHierarchy('Birmingham', $data);
if($found){
echo implode(' -> ', $found);
// Output will be USA -> Alabama -> Birmingham
}
它只能找到一个国家的城市和地方,如果找到任何位置,它将破坏整个功能并返回城市和地方的第一个位置。
这是一个改进的版本,可以找到多个位置。 https://gist.github.com/touqeershafi/bf89351f3b226aae1a29
希望对您有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.