排序数组-子数组在其父数组之后,但保持父数组的位置彼此相对


Sorting array - child after its parent, but keep parents positions to each other

我从数据库数组中得到了这样的结果(用户选择按价格排序):

Array
(
    [0] => Array
        (
            [id] => 812
            [price] => 0
            [par_id] => 310
        )
    [1] => Array
        (
            [id] => 445
            [price] => 3400
            [par_id] => 0
        )
    [2] => Array
        (
            [id] => 1102
            [price] => 3500
            [par_id] => 0
        )
    [3] => Array
        (
            [id] => 310
            [price] => 3700
            [par_id] => 0
        )
    [4] => Array
        (
            [id] => 311
            [price] => 3700
            [par_id] => 310
        )
    [5] => Array
        (
            [id] => 800
            [price] => 3900
            [par_id] => 310
        )
)

我需要将此数组排序为par_id==0的项,在它们下面是它的子项child.par_id=parent.id。要获得带有这些contidion的数组,我使用函数usort():

usort($array,"cmp");

function cmp($a, $b) {
  if ( $a['id'] == $b['id'] ) {
    return 0;
  } else if ( $a['par_id'] ) {
    if ( $a['par_id'] == $b['par_id'] ) {
       return ( $a['id'] < $b['id'] ? -1 : 1 );
    } else {
      return ( $a['par_id'] >= $b['id'] ? 1 : -1 );
    }
  } else if ( $b['par_id'] ) {
    return ( $b['par_id'] >= $a['id'] ? -1 : 1);
  } else {
    return ( $a['id'] < $b['id'] ? -1 : 1 );
  }
}

它工作,然后我得到这样的数组:

Array
(
    [0] => Array
        (
            [id] => 310
            [price] => 3700
            [par_id] => 0
        )
    [1] => Array
        (
            [id] => 311
            [price] => 3700
            [par_id] => 310
        )
    [2] => Array
        (
            [id] => 800
            [price] => 3900
            [par_id] => 310
        )
    [3] => Array
        (
            [id] => 812
            [price] => 0
            [par_id] => 310
        )
    [4] => Array
        (
            [id] => 445
            [price] => 3400
            [par_id] => 0
        )
    [5] => Array
        (
            [id] => 1102
            [price] => 3500
            [par_id] => 0
        )
)

但是,这个数组不是按价格排序的。我的问题是,如果可以在上述状态下对数组进行排序,但也可以保持按价格排序,至少在父级(在子级也是理想情况)。所以我想要得到的结果数组是这样的:

Array
(
    [0] => Array
        (
            [id] => 445
            [price] => 3400
            [par_id] => 0
        )
    [1] => Array
        (
            [id] => 1102
            [price] => 3500
            [par_id] => 0
        )
    [2] => Array
        (
            [id] => 310
            [price] => 3700
            [par_id] => 0
        )
    [3] => Array
        (
            [id] => 812
            [price] => 0
            [par_id] => 310
        )
    [4] => Array
        (
            [id] => 311
            [price] => 3700
            [par_id] => 310
        )
    [5] => Array
        (
            [id] => 800
            [price] => 3900
            [par_id] => 310
        )
)

父级优先,紧随其后的是其子级AND,仍然按用户选择的价格排序。

好问题。您不会在cmp函数中比较价格,因此生成的列表不按价格排序是合乎逻辑的。查看以下cmp函数:

function cmp($a, $b) {
    if ($a['id'] === $b['id']) {
        return 0;
    }
    // both are child or both are parents, so compare by price
    $comparePrices = ($a['par_id'] && $b['par_id'])
        || (!$a['par_id'] && !$b['par_id']);
    $priceResult = $a['price'] > $b['price'] ? 1 : -1;
    if ($comparePrices) {
        return $priceResult;
    } else if ($a['par_id']) {
        // a is a child, so check if b is the parent
        return $a['par_id'] === $b['id'] ? 1 : $priceResult;
    } else if ($b['par_id']) {
        // b is a child, so check if a is the parent
        return $b['par_id'] === $a['id'] ? -1 : $priceResult;
    }
}

这应该有效,但不能保证它适用于所有情况。

出于可读性的考虑,您无论如何都应该实现这种不同的方式。将您的查询一分为二。第一个按价格获取所有父项和订单,第二个按父项id然后按价格获取全部子项和订单。这些数组的合并操作是foreacharray_map的简单组合。例如:

$childItemsByParentId = [];
foreach ($childItems as $childItem) {
    $childItemsByParentId[$childItem['par_id']][] = $childItem;
}
$parentItems = array_map(function ($parentItem) use ($childItemsByParentId) {
    $childs = isset($childItemsByParentId[$parentItem['id']])
        ? $childItemsByParentId[$parentItem['id']]
        : [];
    $parentItem['childs'] = $childs;
    return $parentItem;
}, $parentItems);

我最终发明了另一种方法。

它遍历整个数组,找出实际项是父项还是子项。如果它是子对象,它将再次遍历数组以找到其父对象。如果找到了父项,它会向前或向后移动子项和父项位置之间的所有项(取决于子项在哪里——是在父项的前面还是后面),以在数组中形成一个空间,子项将放置在哪里。

$last_id = 0;
for($i = 0; $i < count($data); $i++) {
      $item = $data[$i];
      if ($item['par_id'] == 0) {
        $last_id = $item['id'];
        continue;
      }
      if ($last_id == $item['par_id']) {
        continue;
      }
      foreach($data as $d => $dat) {
        if ($item['par_id'] == $dat['id']) {
          if ($i < $d) {
            for($j = $i; $j < $d; $j++) { $data[$j] = $data[$j + 1]; }
            $empty = $d;
            $i -= 1;
          } else {
            for($j = $i; $j > $d; $j--) { $data[$j] = $data[$j - 1]; }
            $empty = $d + 1;
          }
          $data = $item;
    } } }