递归数组搜索-保持正确的顺序并迭代数组

时间:2018-09-28 22:59:36

标签: php arrays sorting multidimensional-array

访问我编写的脚本时,访问页面时将类别路径传递给它。然后,脚本会将数据与应与该类别相关联的实际类别数组或分支进行比较。

我将父母及其所有子代设置为tree,然后在分支下进行比较并比较数据,以确保客户使用正确的url。这是代码工作原理的快速示例:

// Customer is accessing from site.com/store/some-cat/some-othercat
// We pass those variables with the htaccess to script.php?var=$1,$2
// We then explode that to make an array on $var[0] and $var[1]
$categoryMap = explode(",", $_GET['var']);
$categoryID = array();
$categoryInfoMap = array();
foreach ($categoryMap as $a) {
    $categoryIDs[] = trim($a);
}
$getCategoryInfo = $db->fn->query("SELECT * FROM store_category");
....
// Inside while loop...
     $categoryInfoMap[] = $db->result[]; // stored whole results as array
// End of the while loop
$masterKey = $mainClass->findKeyInDbArray($categoryInfoMap, 'c.path', $categoryMap[0]);
if ((isset($masterKey) && $masterKey === "0") || !empty($masterKey)) {
    $thisId = $categoryInfoMap[$masterKey]['c.id'];
    $thisPath = $categoryInfoMap[$masterKey]['c.path'];
    $thisName = $categoryInfoMap[$masterKey]['c.name'];
    $tree = $mainClass->buildTree($categoryInfoMap);
    $children = $tree['children'][$thisId];
    $childrenItems = "";
    foreach ($categoryIDs as $cid) {
        // One of the categories entered doesnt exist at all so we redirect,
        // else we will go through them and make sure theyre apart of the branch
        if (!$mainClass->recursive_array_search($cid, $tree)) {
            ... redirect them somewhere and die()
        } else {
            if (!$mainClass->recursive_array_search($cid, $children)) {
                ... redirect them somewhere and die()
            } else {
                !!!!!!!!!!!!============!!!!!!!!!!!!!!
                    THIS IS THE IMPORTANT PART HERE
                !!!!!!!!!!!!============!!!!!!!!!!!!!!
            }
        }
    }
}
... Rest of the script which works for now

以下是上面代码中使用的功能

public function findKeyInDbArray($products, $field, $value) {
    foreach($products as $key => $product) {
        if ($product[$field] === $value) {
            return "$key";
        }
    }
    return NULL;
}
public function buildTree($arr) {
    $tree = array(
        'children' => array()
    );
    $index = array(0=>&$tree);
    foreach ($arr as $key => $val) {
        $parent = &$index[$val['c.parentcatid']];
        $node = $val;
        $parent['children'][$val['c.id']] = $node;
        $index[$val['c.id']] = &$parent['children'][$val['c.id']];
    }
    return $tree;
}
public function recursive_array_search($needle,$haystack) {
    foreach($haystack as $key=>$value) {
        $current_key=$key;
        if($needle===$value OR (is_array($value) && $this->recursive_array_search($needle,$value) !== false)) {
            return $current_key;
        }
    }
    return false;
}

这是从父节点开始向下的树数组的示例。由于可见性原因而被短路

Array(
[c.id] => 1
[c.name] => Radios
[c.path] => radios
[c.parentcatid] => 0
[children] => (
    [2] => (
        [0] => 2
        ....
        [children] => (
            [3] => (
                [c.id] => 3
                ....
                [c.parentcatid] => 2
            ),
            [4] => (
                [c.id] => 4
                ....
                [c.parentcatid] => 2
            )
        )
    )
    ......
    [10] => (
        [0] => 10
        ....
        [c.parentcatid] => 1
    )
)

SO进入好位置

现在,代码正在努力证明分支具有来自其树的匹配变量。如果项目路径(这是我们用来与url $var比较的变量)匹配,则它将继续运行。因此,如果分支中存在以下值:

array(c.path => 'foo'),
array(c.path => 'bar')

我以site.com/store/foo/bar的身份访问脚本,然后一切正常。如果我以site.com/store/foo/notBar的身份访问该站点,则该站点将失败,因为notBar变量不是此分支的成员。那是完美的吧?一切都会正常!除非不是,并且有充分的理由。


此处的问题

如果该项目在分支中匹配,则它已通过检查,这是检查的结束。如果项目以错误的顺序传递,例如site.com/store/bar/foo,则不是这样,但从技术上讲,它仍然具有良好的变量,但是由于结构的排列顺序不符合其从父数组的顺序,因此不应传递。同样,如果树上还有另一个分支,可以说barwithNoChildren存在,即使没有应该,我也可以用它交换foobar并仍然通过>在那里。

希望您能理解我的要求,并可以帮助您提出解决方法。最近几天,我一直在这个系统上动脑筋,由于他们出于seo和其他原因想要精美的网址,所以比我计划的要困难得多。感谢您的任何建议!

1 个答案:

答案 0 :(得分:1)

树结构实际上并没有帮助。您应该考虑如何创建一个数据结构,以使您轻松匹配输入。由于您的类别输入描述了树的分支,因此最好的方法是构建一个数组,您可以使用该数组将这些分支描述与类别有效地匹配。

让我们构建一个数组,其中的键是每个类别的路径(如它们的子句所描述),而值是类别ID。然后,我们可以立即识别出匹配的类别,如果路径不在数组中,则失败。

这种类似于面包屑的结构是类别常用的另一种模式。除了树和平面ID映射外,您几乎可以执行所需的任何操作。关键要点是考虑使用数据创建不同的结构来完成不同的任务。与创建复杂的逻辑来尝试和使用现有的,不适合手头工作的结构相比,创建一种易于使用的新结构通常更高效,更不易出错。

<?php
//Mock category records, would come from the DB in the real world
$categoryRecords = [
    ['id' => 1, 'title' => 'Radios', 'slug' => 'radios', 'parent_id' => 0],
    ['id' => 2, 'title' => 'Accessories', 'slug' => 'misc', 'parent_id' => 1],
    ['id' => 3, 'title' => 'Motorola', 'slug' => 'motorola', 'parent_id' => 1],
    ['id' => 4, 'title' => 'Handheld', 'slug' => 'handheld', 'parent_id' => 3],
    ['id' => 5, 'title' => 'Mobile', 'slug' => 'mobile', 'parent_id' => 3]
];

//Create an array that maps parent IDs to primary keys
$idMap = [];
foreach ($categoryRecords as $currRecord)
{
    $idMap[$currRecord['id']] = $currRecord;
}

//Traverse the flat array and build the path lines
$paths = [];
$categoryIds = array_keys($idMap);
foreach ($categoryIds as $currLeafId)
{
    $currCategoryId = $currLeafId;

    $currLine = [];

    do
    {
        $currLine[]     = $idMap[$currCategoryId]['slug'];
        $currCategoryId = $idMap[$currCategoryId]['parent_id'];
    } while ($currCategoryId != 0);

    $currLine = array_reverse($currLine);
    $currPath = implode('/', $currLine);
    $paths[$currPath] = $currLeafId;
}

//Join your input - $_GET['var'] in your example
$inputPath = implode('/', ['radios', 'motorola', 'handheld']);

//Now you can see if the incoming path matched a category
if(array_key_exists($inputPath, $paths))
{
    $category = $categoryRecords[$paths[$inputPath]];

    echo 'Matched category: '.$category['title'].PHP_EOL;
}
else
{
    echo 'Invalid category path';
}