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

如何实现带点分序列号的对象数组删除后的重排?

带点分版本序列号的数组删除与重排实现方案

示例数据

const sampleData = [
  { name: 'Edward', serialNumber: '1' },
  { name: 'Sharpe', serialNumber: '1.1' },
  { name: 'And', serialNumber: '1.2' },
  { name: 'The', serialNumber: '1.2.1' },
  { name: 'Magnetic', serialNumber: '2' },
  { name: 'Zeros', serialNumber: '2.1' },
  { name: 'Zeros', serialNumber: '2.1.1' },
  { name: 'shappa', serialNumber: '3' },
  { name: 'Firo', serialNumber: '4' },
  { name: 'Dholya', serialNumber: '4.1' },
  { name: 'dhampya', serialNumber: '4.1.1' },
  { name: 'SHivaji', serialNumber: '4.2' },
  { name: 'Jyoti', serialNumber: '4.2.1' },
  { name: 'Shr', serialNumber: '4.2.2' }
];

删除与重排需求

  • 场景1:删除序列号为'3'的对象后,原序列号4及所有子节点的序列号调整为3及对应子级(比如4→3,4.1→3.1,以此类推)
  • 场景2:删除序列号为'4'的对象后,同时移除其所有子节点(即删除4、4.1、4.1.1、4.2、4.2.1、4.2.2)
  • 场景3:删除序列号为'4.1'的对象及其子节点后,原4.2及子节点的序列号调整为4.1及对应子级(比如4.2→4.1,4.2.1→4.1.1)

最优实现方案

核心思路

  1. 先筛选出需要保留的节点:排除目标节点及其所有子节点
  2. 将保留的节点按层级排序,再按父节点分组重新生成连续的点分序列号

工具函数与核心实现

// 判断某序列号是否是目标序列号的子节点或自身
function isDescendantOrSelf(serial, targetSerial) {
  const targetParts = targetSerial.split('.').map(Number);
  const serialParts = serial.split('.').map(Number);
  if (serialParts.length < targetParts.length) return false;
  // 逐段匹配前缀,完全匹配则为目标节点或其子节点
  for (let i = 0; i < targetParts.length; i++) {
    if (serialParts[i] !== targetParts[i]) return false;
  }
  return true;
}

// 对保留节点重新生成连续序列号
function renumberNodes(nodes) {
  // 按序列号层级和数值排序,确保父节点在前、子节点在后
  nodes.sort((a, b) => {
    const aParts = a.serialNumber.split('.').map(Number);
    const bParts = b.serialNumber.split('.').map(Number);
    const maxLen = Math.max(aParts.length, bParts.length);
    for (let i = 0; i < maxLen; i++) {
      const aVal = aParts[i] || 0;
      const bVal = bParts[i] || 0;
      if (aVal !== bVal) return aVal - bVal;
    }
    return 0;
  });

  // 记录每个父节点下的子节点计数,用于生成新序列号
  const parentCounter = new Map();
  const result = [];

  for (const node of nodes) {
    const originalParts = node.serialNumber.split('.').map(Number);
    // 生成父节点路径(去掉当前节点的最后一段序号)
    const parentPath = originalParts.slice(0, -1).join('.') || '';
    
    // 获取当前父级下的子节点序号,初始为1
    const currentCount = (parentCounter.get(parentPath) || 0) + 1;
    parentCounter.set(parentPath, currentCount);

    // 拼接新序列号
    const newSerialParts = parentPath ? [...parentPath.split('.').map(Number), currentCount] : [currentCount];
    const newSerial = newSerialParts.join('.');

    result.push({ ...node, serialNumber: newSerial });
  }

  return result;
}

// 主函数:删除目标节点并重排序列号
function deleteAndRenumber(originalData, targetSerial) {
  // 过滤掉目标节点及其所有子节点
  const filteredNodes = originalData.filter(node => !isDescendantOrSelf(node.serialNumber, targetSerial));
  // 对剩余节点重新编号
  return renumberNodes(filteredNodes);
}

场景验证

场景1:删除'3'

const result1 = deleteAndRenumber(sampleData, '3');
console.log(result1.map(n => ({name: n.name, serialNumber: n.serialNumber})));
// 输出:
// [
//   { name: 'Edward', serialNumber: '1' },
//   { name: 'Sharpe', serialNumber: '1.1' },
//   { name: 'And', serialNumber: '1.2' },
//   { name: 'The', serialNumber: '1.2.1' },
//   { name: 'Magnetic', serialNumber: '2' },
//   { name: 'Zeros', serialNumber: '2.1' },
//   { name: 'Zeros', serialNumber: '2.1.1' },
//   { name: 'Firo', serialNumber: '3' },
//   { name: 'Dholya', serialNumber: '3.1' },
//   { name: 'dhampya', serialNumber: '3.1.1' },
//   { name: 'SHivaji', serialNumber: '3.2' },
//   { name: 'Jyoti', serialNumber: '3.2.1' },
//   { name: 'Shr', serialNumber: '3.2.2' }
// ]

场景2:删除'4'

const result2 = deleteAndRenumber(sampleData, '4');
console.log(result2.map(n => ({name: n.name, serialNumber: n.serialNumber})));
// 输出:
// [
//   { name: 'Edward', serialNumber: '1' },
//   { name: 'Sharpe', serialNumber: '1.1' },
//   { name: 'And', serialNumber: '1.2' },
//   { name: 'The', serialNumber: '1.2.1' },
//   { name: 'Magnetic', serialNumber: '2' },
//   { name: 'Zeros', serialNumber: '2.1' },
//   { name: 'Zeros', serialNumber: '2.1.1' },
//   { name: 'shappa', serialNumber: '3' }
// ]

场景3:删除'4.1'

const result3 = deleteAndRenumber(sampleData, '4.1');
console.log(result3.map(n => ({name: n.name, serialNumber: n.serialNumber})));
// 输出:
// [
//   { name: 'Edward', serialNumber: '1' },
//   { name: 'Sharpe', serialNumber: '1.1' },
//   { name: 'And', serialNumber: '1.2' },
//   { name: 'The', serialNumber: '1.2.1' },
//   { name: 'Magnetic', serialNumber: '2' },
//   { name: 'Zeros', serialNumber: '2.1' },
//   { name: 'Zeros', serialNumber: '2.1.1' },
//   { name: 'shappa', serialNumber: '3' },
//   { name: 'Firo', serialNumber: '4' },
//   { name: 'SHivaji', serialNumber: '4.1' },
//   { name: 'Jyoti', serialNumber: '4.1.1' },
//   { name: 'Shr', serialNumber: '4.1.2' }
// ]

方案优势

  • 通用性:一套代码覆盖所有三种场景,无需针对不同层级编写单独逻辑
  • 稳定性:先排序再重编号,确保父节点始终在子节点之前,避免编号混乱
  • 可扩展性:支持任意深度的点分序列号,新增层级无需修改核心逻辑

内容的提问来源于stack exchange,提问作者A.K.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:15:40