对数组进行排序,将孩子置于父母之下

时间:2011-06-16 19:01:52

标签: php arrays sorting parent

所以我在php中有一个项目数组,有些可能通过parent_id键链接到其他项目。我正在寻找这个数组的排序,以便其父项在此数组中的任何项目最终位于父项目的正下方。

示例:(实际数组有更多键)

some_array[0]['id'] = 15001;
some_array[0]['parent_id'] = 14899;

some_array[1]['id'] = 14723;
some_array[1]['parent_id'] = 0; //parent_id of 0 means item has no parent of its own

some_array[2]['id'] = 14899;
some_array[2]['parent_id'] = 0;

some_array[3]['id'] = 15000;
some_array[3][parent_id'] = 14723;

我想对这些进行排序,以便按顺序排列:

some_array[0]['id'] = 14723;

some_array[1]['id'] = 15000;

some_array[2]['id'] = 14899;

some_array[3]['id'] = 15001;

即。物品就在父母的下方。

提前致谢!

5 个答案:

答案 0 :(得分:5)

我怀疑你们还在寻找一个真正的答案,但它可能会帮助其他人解决同样的问题。下面是一个递归函数,用于使用将子项放在父母下面的数组。

$initial = array(
    array(
        'name' => 'People',
        'ID' => 2,
        'parent' => 0
        ),
    array(
        'name' => 'Paul',
        'ID' => 4,
        'parent' => 2
        ),
    array(
        'name' => 'Liz',
        'ID' => 5,
        'parent' => 2
        ),
    array(
        'name' => 'Comus',
        'ID' => 6,
        'parent' => 3
        ),
    array(
        'name' => 'Mai',
        'ID' => 7,
        'parent' => 2
        ),
    array(
        'name' => 'Titus',
        'ID' => 8,
        'parent' => 3
        ),
    array(
        'name' => 'Adult',
        'ID' => 9,
        'parent' => 6
        ),
    array(
        'name' => 'Puppy',
        'ID' => 10,
        'parent' => 8
        ),
    array(
        'name' => 'Programmers',
        'ID' => 11,
        'parent' => 4
        )   ,
    array(
        'name' => 'Animals',
        'ID' => 3,
        'parent' => 0
        )                           
    );


/*---------------------------------
function parentChildSort_r
$idField        = The item's ID identifier (required)
$parentField    = The item's parent identifier (required)
$els            = The array (required)
$parentID       = The parent ID for which to sort (internal)
$result     = The result set (internal)
$depth          = The depth (internal)
----------------------------------*/

function parentChildSort_r($idField, $parentField, $els, $parentID = 0, &$result = array(), &$depth = 0){
    foreach ($els as $key => $value):
        if ($value[$parentField] == $parentID){
            $value['depth'] = $depth;
            array_push($result, $value);
            unset($els[$key]);
            $oldParent = $parentID; 
            $parentID = $value[$idField];
            $depth++;
            parentChildSort_r($idField,$parentField, $els, $parentID, $result, $depth);
            $parentID = $oldParent;
            $depth--;
        }
    endforeach;
    return $result;
}

$result = parentChildSort_r('ID','parent',$initial);

print '<pre>';
print_r($result);
print '</pre>';

这是一种减速方法,可以从原始数组中删除元素,并按正确的顺序将它们放入结果集中。我为你做了一些通用的,所以它只需要你告诉它你的'ID'字段和'parent'字段被调用。顶级项目需要将parent_id(但是您将其命名为)为0。

答案 1 :(得分:4)

我的较短版本的matwang的回答:

/**
 * sort parents before children
 *
 * @param array   $objects input objects with attributes 'id' and 'parent'
 * @param array   $result  (optional, reference) internal
 * @param integer $parent  (optional) internal
 * @param integer $depth   (optional) internal
 * @return array           output
 */
function parent_sort(array $objects, array &$result=array(), $parent=0, $depth=0) {
    foreach ($objects as $key => $object) {
        if ($object->parent == $parent) {
            $object->depth = $depth;
            array_push($result, $object);
            unset($objects[$key]);
            parent_sort($objects, $result, $object->id, $depth + 1);
        }
    }
    return $result;
}

唯一的区别是它对一个对象数组进行排序,而不是对数组数组进行排序。

答案 2 :(得分:3)

您可以使用usort按用户定义的功能排序:

function cmp($a, $b)
{
  if ( $a['id'] == $b['id'] ) {
    return 0;

  } else if ( $a['parent_id'] ) {
    if ( $a['parent_id'] == $b['parent_id'] ) {
       return ( $a['id'] < $b['id'] ? -1 : 1 );
    } else {
      return ( $a['parent_id'] >= $b['id'] ? 1 : -1 );
    }
  } else if ( $b['parent_id'] ) {
    return ( $b['parent_id'] >= $a['id'] ? -1 : 1);
  } else {
    return ( $a['id'] < $b['id'] ? -1 : 1 );
  }
}

usort($some_array, "cmp");

注意:这只适用于深度为一级的树(意味着没有孩子的孩子)。对于更复杂的树,您可能希望将数据排序为图形,然后将其展平。

已修复

修改 以编辑$b有父级但$a没有父级的情况。

答案 3 :(得分:0)

只需使用usort()功能,并以您需要的方式比较“大数组”的两个不同元素。这成了一个关于'我如何确定哪个元素在哪个元素之前的问题?'。

答案 4 :(得分:0)

如果您想支持多个子层,那么简单的用户将无效。没有其他信息,根本无法知道两个任意元素的比较。

我没有想太多,所以也许它不起作用。但这是一个排序类:

class TopSort
{
  private $sorted, $unsorted;
  private $history;

  public function sort(array $unsorted)
  {
    $this->sorted = array();
    $this->unsorted = $unsorted;
    $this->history = array();

    usort($this->unsorted, function($a, $b)
    {
      return $b['id'] - $a['id'];
    });

    foreach ($this->unsorted as $i => $a)
      if ($a['parent_id'] == 0) $this->visit($i);

    return array_reverse($this->sorted);
  }

  private function visit($i)
  {
    if (!array_key_exists($i, $this->history))
    {
      $this->history[$i] = true;
      foreach ($this->unsorted as $j => $a)
        if ($a['parent_id'] == $this->unsorted[$i]['id']) $this->visit($j);

      $this->sorted[] = $this->unsorted[$i];
    }
  }
}

$sorter = new TopSort();
$some_array = $sorter->sort($some_array);

这里的想法是首先按id反向排序。然后通过首先插入最深的元素(没有子元素)来构建一个新数组。因为我们最初通过反向id对数组进行排序,所以它应该意味着整个事物是颠倒的。反转阵列后,应该完全符合您的要求。 (当然可以将数据移到数组上以避免反向操作,但这可能会更慢......)

这是非常未经优化,因为它遍历整个阵列很多次。只需稍加返工,就不需要这样做了。

这是一个更优化的替代类:

class TopSort
{
  private $sorted;

  public function sort(array $nodes)
  {
    $this->sorted = array();

    # sort by id
    usort($nodes, function($a, $b) {
      return $a['id'] - $b['id'];
    });

    # build tree
    $p = array(0 => array());
    foreach($nodes as $n)
    {
      $pid = $n['parent_id'];
      $id = $n['id'];

      if (!isset($p[$pid]))
        $p[$pid] = array('child' => array());

      if (isset($p[$id]))
        $child = &$p[$id]['child'];
      else
        $child = array();

          $p[$id] = $n;
      $p[$id]['child'] = &$child;
      unset($child);

      $p[$pid]['child'][] = &$p[$id];    
    }
    $nodes = $p['0']['child'];
    unset($p);

    # flatten array
    foreach ($nodes as $node)
      $this->flatten($node);

    return $this->sorted;
  }

  private function flatten(array $node)
  {
    $children = $node['child'];
    unset($node['child']);
    $this->sorted[] = $node;
    foreach ($children as $node)
      $this->flatten($node);
  }
}

$sorter = new TopSort();
$sorted = $sorter->sort($some_array);

这是一个三步法:

  1. 按ID(usort)排序
  2. 构建嵌套数组结构。
  3. 预先整理数组。
  4. 根据id的预先分类,每组儿童都应该正确分类。

相关问题