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

TypeScript动态映射排序数据源键值报undefined错误排查

需求说明

现有存储多个对象的大型数据源(实际对象的键名、键数量未知),需要实现排序规则:

  • 遍历所有对象,对每个对象的任意属性键,只要检测到该键下出现从未记录过的新值,就将当前所属对象移动到数组靠前位置
  • 后续首次出现新值的对象依次排在第2位、第3位……最终数组最靠前的x个元素,刚好可以覆盖所有属性下全部的不同取值

示例输入

const arr = [
  {name: 'john', age: 14, adress: 'xxx'}, // 索引0
  {name: 'john', age: 14, adress: 'xxx'}, // 索引1
  {name: 'marie', age: 14, adress: 'xxx'}, // 索引2
  {name: 'marie', age: 14, adress: 'xxx'}, // 索引3
  {name: 'john', age: 15, adress: 'xxx'}  // 索引4
]

期望输出

[
  {name: 'john', age: 14, adress: 'xxx'}, // 首次出现name:john、age:14、adress:xxx,排第0位
  {name: 'marie', age: 14, adress: 'xxx'}, // 首次出现name:marie,排第1位
  {name: 'john', age: 15, adress: 'xxx'}, // 首次出现age:15,排第2位
  {name: 'marie', age: 14, adress: 'xxx'}, // 无新值,保留靠后位置
  {name: 'john', age: 14, adress: 'xxx'}  // 无新值,保留靠后位置
]
原代码报错&逻辑问题

代码运行中断、逻辑不符合预期的核心问题有4个:

  1. uniqueColumns只做了变量声明,没有初始化赋值,直接访问uniqueColumns[key]会得到undefined,再调用.push()直接触发类型错误中断,这是最直接的崩溃原因。
  2. 没有做值的重复判断:不管当前键值是不是已经出现过,只要遍历到键就做交换、自增计数指针,完全不符合“只有首次出现新值才移动对象”的规则,会导致大量无意义交换,顺序完全混乱。
  3. 同一个对象如果同时有多个键携带新值,会被重复交换多次,导致计数指针i错误,甚至同一个对象重复占据多个前排位置。
  4. 每次交换都用indexOf查找当前对象索引,大数组场景下性能极差,且遍历过程中数组被持续修改,很容易拿到错误索引值,引发更多异常。
修复后的可运行实现

针对大数据源场景做了性能优化,不需要提前感知对象结构,直接适配任意键名、键数量的对象数组:

function filterDS(dataSource) {
  // 初始化无原型对象存储各键已出现的值,避免原型链属性干扰
  const seenValues = Object.create(null);
  // 下一个可放置带新值对象的前排位置
  let insertPointer = 0;
  const total = dataSource.length;

  for (let curIdx = 0; curIdx < total; curIdx++) {
    const curItem = dataSource[curIdx];
    const keys = Object.keys(curItem);
    let hasNewValue = false;

    // 遍历当前对象所有键,检查是否存在未记录的新值
    for (const key of keys) {
      const val = curItem[key];
      // 第一次遇到该键,初始化对应的值集合
      if (!seenValues[key]) {
        seenValues[key] = new Set();
      }
      // 检测到新值,标记当前对象需要前移
      if (!seenValues[key].has(val)) {
        seenValues[key].add(val);
        hasNewValue = true;
      }
    }

    // 仅当对象携带至少一个新值时,才移动到前排位置
    if (hasNewValue) {
      // 当前位置已经是待插入位置,不需要交换
      if (curIdx !== insertPointer) {
        const temp = dataSource[insertPointer];
        dataSource[insertPointer] = curItem;
        dataSource[curIdx] = temp;
      }
      insertPointer++;
    }
  }

  return dataSource;
}
实现说明
  • 用Set存储每个键下已出现的值,判重时间复杂度为O(1),相比数组的includes(O(n))更适合大型数据源场景。
  • 单个对象就算同时携带多个新值,也只会被移动一次,不会重复占用前排位置。
  • 全程只在检测到新值时做一次交换操作,不需要反复查找元素索引,不会出现索引错乱问题。
  • 最终insertPointer的值就是覆盖所有属性不同取值需要的最少元素个数,数组前insertPointer个元素刚好满足全覆盖要求。

内容的提问来源于stack exchange,提问作者haddadtheorc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:27:19