You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现:选中行按指定起始位置重排display_order

问题描述

面试CRUD任务中的排序重排需求:需按display_order列排序展示数据,支持勾选多行后指定起始位置重排,规则如下:

  • 示例1:起始位置为2时,勾选行(id:86、77、44)从位置2开始依次排列,其余未勾选行按原展示顺序(排除已勾选行)依次后移
  • 示例2:起始位置为8时,勾选行从位置8开始排列,原位置8及之后的未勾选行自动后移

现有reorder_list函数输出结果不符合预期,需调试修正。


现有代码

def reorder_list(queryset, sortable_data):
    selected_ids = sortable_data.get('selected_ids', [])
    start_position = sortable_data.get('start_position', 1)
    # 分离选中和未选中的项
    selected_items = [item for item in queryset if item.id in selected_ids]
    unselected_items = [item for item in queryset if item.id not in selected_ids]
    
    # 重新排序
    reordered = []
    # 添加起始位置之前的未选中项
    reordered.extend(unselected_items[:start_position-1])
    # 添加选中项
    reordered.extend(selected_items)
    # 添加剩余的未选中项
    reordered.extend(unselected_items[start_position-1:])
    
    # 更新display_order
    for index, item in enumerate(reordered, start=1):
        item.display_order = index
    return reordered

测试数据

QuerySet数据(按display_order初始排序)

iddisplay_order
1011
952
863
774
625
446
317
228
159

Sortable请求数据

示例1请求

{
    "selected_ids": [86,77,44],
    "start_position": 2
}

预期结果:

iddisplay_order
1011
862
773
444
955
626
317
228
159

示例2请求

{
    "selected_ids": [86,77,44],
    "start_position": 8
}

预期结果:

iddisplay_order
1011
952
623
314
225
156
867
778
449

问题分析

原代码的核心错误:

  1. 未保证输入顺序正确性:直接使用传入的queryset,但QuerySet默认排序可能不是按display_order,导致后续分离选中/未选中项的顺序与展示顺序不符。
  2. 起始位置切片逻辑错误:unselected_items是排除选中项后的列表,其索引与原始展示顺序的位置无对应关系,直接用unselected_items[:start_position-1]会导致起始位置前后的项截取错误。

修正后的代码

def reorder_list(queryset, sortable_data):
    selected_ids = set(sortable_data.get('selected_ids', []))
    start_position = sortable_data.get('start_position', 1)
    
    # 确保按display_order排序,保证处理顺序与展示顺序一致
    sorted_items = sorted(queryset, key=lambda x: x.display_order)
    
    selected = []
    pre_unselected = []
    post_unselected = []
    
    # 遍历排序后的项,按规则划分三组
    for item in sorted_items:
        if item.id in selected_ids:
            selected.append(item)
        else:
            # 原始位置从1开始计数
            original_pos = sorted_items.index(item) + 1
            if original_pos < start_position:
                pre_unselected.append(item)
            else:
                post_unselected.append(item)
    
    # 拼接最终排序列表
    reordered = pre_unselected + selected + post_unselected
    
    # 更新display_order字段
    for idx, item in enumerate(reordered, start=1):
        item.display_order = idx
    
    return reordered

优化版(避免O(n)的index查询)

def reorder_list(queryset, sortable_data):
    selected_ids = set(sortable_data.get('selected_ids', []))
    start_position = sortable_data.get('start_position', 1)
    
    # 按display_order排序,同时记录原始位置
    sorted_with_pos = [(item, idx+1) for idx, item in enumerate(sorted(queryset, key=lambda x: x.display_order))]
    
    selected = []
    pre_unselected = []
    post_unselected = []
    
    for item, original_pos in sorted_with_pos:
        if item.id in selected_ids:
            selected.append(item)
        else:
            if original_pos < start_position:
                pre_unselected.append(item)
            else:
                post_unselected.append(item)
    
    reordered = pre_unselected + selected + post_unselected
    
    for idx, item in enumerate(reordered, start=1):
        item.display_order = idx
    
    return reordered

代码说明

  1. 强制排序:先将QuerySet按display_order排序,确保后续处理的是正确的展示顺序。
  2. 高效划分:使用集合存储选中ID,将判断操作的时间复杂度从O(n)降到O(1)。
  3. 精准分组:根据每个未选中项在原始展示顺序中的位置,划分到起始位置前/后两组,保证未选中项的相对顺序不变。
  4. 正确拼接:按「起始位置前的未选中项 → 选中项 → 起始位置后的未选中项」的顺序拼接,完全符合需求规则。

内容的提问来源于stack exchange,提问作者Hey It's Cheff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:25:00