首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >按两列对行数组进行排序,同时不移动“锁定”(粘滞)行

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

Stack Overflow用户
提问于 2022-08-26 00:17:17
回答 2查看 93关注 0票数 -1

我需要按分数排序一个项目数组,然后按日期排序,而不移动锁值大于零的行。

换句话说,在排序完成后,带有lock=3lock=5的行应该保持在相同/原始的位置。

代码语言:javascript
复制
[
    ['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],
]

我使用下面的代码对score进行排序,而不是在strtotime上排序,但这会影响不应该移动的行。

代码语言:javascript
复制
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
});

我想要的输出是:

代码语言:javascript
复制
[
    ['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],
]
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-08-26 00:54:23

在排序过程中,您无法访问列表中的绝对位置,只有要比较的一对项,因此我将这样处理它:

  1. 将“锁定”值从列表中删除
  2. 把其他的东西分类
  3. 将锁定的值放回

对于步骤1,只需遍历数组,生成两个新数组:

代码语言:javascript
复制
$result = [];
$locked = [];
foreach ( $input as $item ) {
    if ( $item['lock'] > 0 ) {
        $locked[] = $item;
    }
    else {
        $result[] = $item;
    }
}

步骤2是您已经拥有的代码,它使用了解锁项数组(我称之为$result ),因为它最终将包含最终结果。

对于第三步,您可以使用接合在选定的位置将一个项放入数组中,然后将所有内容向下移动。

这里需要注意的一点是,您在事项中插入的顺序是:如果您将项目X插入位置5,则将项目Y移至位置3,则位置X将向前移至位置6。因此,如果锁定的项目尚未排序,请对它们进行排序:

代码语言:javascript
复制
usort($locked, fn($a,$b) => $a['lock'] <=> $b['lock']);

然后绕过去,把它们拼接到他们想要的位置上:

代码语言:javascript
复制
foreach ( $locked as $item ) {
    array_splice($result, $item['lock'], 0, $item);
}

然后你就应该完成:)

票数 1
EN

Stack Overflow用户

发布于 2022-08-27 10:58:20

在没有任何过滤、临时数组或剪接的情况下,我已经构建了一个手动排序算法,其中包含三个嵌套循环(具有最佳性能的条件短路),以提供“粘性排序”。

没有函数调用,因此它将执行得相当快。我添加了内联注释以帮助理解代码。

下面的代码段并不关心非零锁值是什么。在发布的问题中,锁定的行已经位于结果中它们应该位于的位置。该算法从不移动具有非零锁值的行。这使得脚本很容易适应另一个标志指示“固定行”的其他场景。

代码:(演示)

代码语言:javascript
复制
$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);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73494836

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档