简体   繁体   English

按两列对行数组进行排序,同时不移动“锁定”(粘性)行

[英]Sort an array of rows by two columns while not moving "locked" (sticky) rows

I need to sort an array of items by score, then by date without moving the rows where the lock value is greater than zero.我需要按分数对项目数组进行排序,然后按日期排序,而不移动锁定值大于零的行。

In other words, the rows with lock=3 and lock=5 should stay in the same/original position after the sorting is finished.换句话说, lock=3lock=5的行在排序完成后应该保持在相同/原始的 position 中。

[
    ['id' => 7867867, 'lock' => 0, 'score' => 322, 'strtotime' => 16614713],
    ['id' => 7867867, 'lock' => 0, 'score' => 444, 'strtotime' => 16614613],
    ['id' => 7867867, 'lock' => 3, 'score' =>   0, 'strtotime' => 16613713],
    ['id' => 7867867, 'lock' => 0, 'score' =>  11, 'strtotime' => 16612713],
    ['id' => 7867867, 'lock' => 5, 'score' =>   0, 'strtotime' => 16614413],
    ['id' => 7867867, 'lock' => 0, 'score' =>  42, 'strtotime' => 16614113],
    ['id' => 7867867, 'lock' => 0, 'score' =>  22, 'strtotime' => 16614013],
]

I use the following code to sort on score than strtotime , but this affects the rows that shouldn't move.我使用以下代码对score进行排序而不是strtotime ,但这会影响不应移动的行。

usort($array, function ($a, $b) {
    if ( $a->score == $b->score ) {  //score are same
       return $b->strtotime <=> $a->strtotime; //sort by strtotime
    }
    return $b->score <=> $a->score; //else sort by score
});

My desired output is:我想要的 output 是:

[
    ['id' => 7867867, 'lock' => 0, 'score' =>  11, 'strtotime' => 16612713],
    ['id' => 7867867, 'lock' => 0, 'score' =>  22, 'strtotime' => 16614013],
    ['id' => 7867867, 'lock' => 3, 'score' =>   0, 'strtotime' => 16613713],
    ['id' => 7867867, 'lock' => 0, 'score' =>  42, 'strtotime' => 16614113],
    ['id' => 7867867, 'lock' => 5, 'score' =>   0, 'strtotime' => 16614413],
    ['id' => 7867867, 'lock' => 0, 'score' => 322, 'strtotime' => 16614713],
    ['id' => 7867867, 'lock' => 0, 'score' => 444, 'strtotime' => 16614613],
]

During a sort, you have no access to the absolute position in the list, only the pair of items being compared, so I would approach it this way:在排序过程中,您无法访问列表中的绝对 position,只有正在比较的项目对,所以我会这样处理:

  1. Take the "locked" values out of the list将“锁定”值从列表中取出
  2. Sort everything else对其他所有内容进行排序
  3. Put the locked values back in将锁定的值放回

For step 1, just loop over array, producing two new arrays:对于第 1 步,只需遍历数组,生成两个新的 arrays:

$result = [];
$locked = [];
foreach ( $input as $item ) {
    if ( $item['lock'] > 0 ) {
        $locked[] = $item;
    }
    else {
        $result[] = $item;
    }
}

Step 2 is the code you already have, using the array of unlocked items, which I've called $result , because it will eventually contain the final result.第 2 步是您已经拥有的代码,使用未锁定项目数组,我称之为$result ,因为它最终将包含最终结果。

For step 3, you can use array_splice to put an item into the array at a chosen position, shifting everything afterwards down.对于第 3 步,您可以使用array_splice将项目放入所选 position 的数组中,然后将所有内容向下移动。

An important thing to note here is that the order you insert in matters: if you insert item X into position 5, then item Y into position 3, position X will be shifted forward to position 6. So if your locked items aren't already in order, sort them: An important thing to note here is that the order you insert in matters: if you insert item X into position 5, then item Y into position 3, position X will be shifted forward to position 6. So if your locked items aren't already按顺序对它们进行排序:

usort($locked, fn($a,$b) => $a['lock'] <=> $b['lock']);

Then loop over, and splice them into their desired positions:然后循环,并将它们拼接到所需的位置:

foreach ( $locked as $item ) {
    array_splice($result, $item['lock'], 0, $item);
}

And then you should be done:)然后你应该完成:)

Without any filtering, temporary arrays, or splicing, I've crafted a manual sorting algorithm with three nested loops (with conditional short circuiting for best performance) to provide a "sticky sort".在没有任何过滤、临时 arrays 或拼接的情况下,我制作了一个手动排序算法,其中包含三个嵌套循环(为了获得最佳性能而使用条件短路)来提供“粘性排序”。

There are no function calls, so it will execute rather quickly.没有 function 调用,所以它会很快执行。 I've added inline comments to help with code comprehension.我添加了内联注释来帮助理解代码。

The below snippet does not care what the non-zero lock values are.下面的代码片段不关心非零锁值是什么。 In the posted question, the locked rows are already in the location that they should be in the result.在发布的问题中,锁定的行已经在它们应该在结果中的位置。 The algorithm simply never moves rows with non-zero lock values.该算法根本不会移动具有非零锁定值的行。 This makes the script easily adaptable to other scenarios where another flag indicates "fixed rows".这使得脚本很容易适应另一个标志指示“固定行”的其他场景。

Code: ( Demo )代码:(演示

$maxIndex = count($array) - 1;
for ($a = 0; $a < $maxIndex; ++$a) {
    if ($array[$a]['lock'] !== 0) {
        continue;  // cannot move locked row
    }
    for ($b = 0; $b < $maxIndex; ++$b) {
        if ($array[$b]['lock'] !== 0) {
            continue;  // cannot move locked row
        }
        // find next movable row
        for ($c = $b + 1; $c <= $maxIndex; ++$c) {
            if ($array[$c]['lock'] === 0) {
                break;  // $c is index of non-locked row
            }
        }
        if ($c > $maxIndex) {
            break;  // no more movable rows
        }
        // sort movable rows
        if (
            $array[$b]['score'] > $array[$c]['score']
            || ($array[$b]['score'] === $array[$c]['score']
                && $array[$b]['strtotime'] > $array[$c]['strtotime'])
        ) {
             [$array[$b], $array[$c]] = [$array[$c], $array[$b]];
        }
    }
}
var_export($array);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM