2

我有两个具有键“menu_title”和“id”的数组。

我想遍历这两个数组并将第一个数组中与第二个数组中的数组共享“menu_title”的任何数组的 id 存储在第三个数组中。

我知道我可以这样做:

$collision = [];
foreach($children as $child)
    foreach($siblings as $sibling)
        if($child['menu_title'] == $sibling['menu_title'])
            $collision[] = $child['id'];

但这意味着我要为第一个中的每个项目循环第二个数组。

也许这会更好?

$collision = [];
foreach($siblings as &$sibling)
        $sibling = $sibling['menu_title'];

    foreach($children as $child)
        if(in_array($child['menu_title'], $siblings))
            $collision[] = $child['id'];

但我仍然认为必须有更好的方法?


更新

数组由 sql 填充,基本上如果我删除一个类别,那么我会将子类别移动到与我要删除的类别相同的级别。

但是,如果任何孩子的 menu_title 与删除类别兄弟姐妹之一相同,那么我需要将孩子的 menu_title 重命名为“whatever-1”,例如“computers-1”

所以我正在构建一个我需要更新的 id 数组,然后我将对这些 ID 进行 sql 更新,可能对我如何处理这个问题进行改进?


更新

所以,最后我得到了:

$id = 4;
$category = $this->categoryeditormodel->getCategory($id);
$children = $this->categoryeditormodel->getChildCategories($id);
$siblings = $this->categoryeditormodel->getSiblingCategories($id);

foreach($siblings as &$sibling)
    $sibling = $sibling['menu_title'];

foreach($children as &$child){
    $child['parent_id'] = $category['parent_id'];
    if(in_array($child['menu_title'], $siblings)){
        $i = 0;
        while(in_array(($name = ($child['menu_title'].'-'.++$i)), $siblings));
        $child['menu_title'] = $name;
    }
}
$this->categoryeditormodel->update_batch($children);

顶部的三个函数按照他们说的做,但它们很便宜,因为类别已经加载到缓存中,所以它不是另一个 sql 查询。

update_batch 只是 Code Igniters update_batch 函数的快捷方式,但它通过表名和 id 键传递。

有什么想法吗?

4

4 回答 4

2

本质上这是 O(m*n) 其中 m 和 n 是两个数组的大小——所以你不能做得更好。最后,您需要查看每一对(兄弟姐妹、孩子),除非您可以更具体地了解这些数组的填充方式,否则没有太大的改进空间。

可能会有一些调整来优化这一点,但我没有立即想到,而且可能没有任何东西会产生巨大的差异。

编辑:如果您首先对两个数组进行排序,即 O(n*log n + m*log m),那么您可能会根据数组中的实际内容进行一些渐近改进。除非数组相当大,否则这可能不会有用。

于 2012-08-22T10:33:39.743 回答
1

您可以直接在 SQL 中执行此操作。

我假设类别表类似于(我缩进了标题以显示原始层次结构):

id    parent title
1     0      computers
2     1         laptop
3     1         desktop
4     1         *servers*     <--
5     0      *servers*        <--
6     0      printers

并且您想删除 ID 为 1 的“计算机”,我将其称为 DEL_ID。您可以通过以下方式执行此操作:

  • 检索行 DEL_ID 的信息,尤其是父类别 ID PAR_ID
  • 更新 parent 为 DEL_ID 的所有行,以便它们的 parent 变为 PAR_ID
  • 删除 ID 为 DEL_ID 的行。

执行此操作时,您需要检查具有 DEL_ID 父级的行与具有父级 PAR_ID 的任何行具有相同的标题。当他们这样做时,您会更新他们的名字。

所以在 SQL 中:

UPDATE table SET title = CONCAT(title, '-1') FROM table
    JOIN table AS check ON (
        table.parent = DEL_ID
        AND check.parent = PAR_ID
        AND table.title = check.title);

UPDATE table SET parent = PAR_ID WHERE parent = DEL_ID;
DELETE FROM table WHERE id = DEL_ID;

出于某种原因,如果您在victimCategory 的兄弟姐妹中已经拥有“title”和“title-1”,并且它的一个孩子又是“title”,则可能会出现问题。在这种情况下,将 'title' 重命名为 'title-1' 将导致冲突(与您在 PHP 实现中遇到的问题相同)。

您可以通过将第一个 UPDATE 转换为 SELECT 并检索类别的“旧”和“建议”标题来检测这种情况,然后运行新的 SELECT 以验证您是否接受“建议”名称没有重复;并重复递增 -1、-2 等后缀,直到检查选择返回没有冲突的行。LIKE或者您可以一举检索旧“title%”的所有标题,如果有任何标题(意味着您会遇到冲突),请以数字而非字典顺序选择更大的标题 - 即 title-11 > title-2 - 将其编号加一并将其用于新更新,现在必须逐一进行。它/是/昂贵的,但我想它很少发生。

于 2012-08-22T10:54:14.677 回答
0

我认为没有更好的方法来做到这一点。如果要比较两个数组,则必须循环遍历它们。您可以像在数据库中一样创建索引,但如果您的数组不包含 >1000?不值得的元素。

无论如何,要创建一个索引:

$index = array();
foreach($siblings as $sibling)
{
  $index[$sibling['menu_title']] = true;
}
foreach($children as $child)
{
  if (isset($index[$child['menu_title']])) $collision[] = $child['id'];
}
于 2012-08-22T10:34:23.857 回答
0
$collision = array_intersect($children, $siblings);

那应该为您提供具有重复值的数组。检查array_intersect

于 2012-08-22T10:37:14.577 回答