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

如何按前缀或键将JSON键分组为新键?求JavaScript高效实现方案

问题:如何高效转换Knex查询结果的嵌套结构?

我用Knex查询后得到的结果是扁平化的对象数组,键名带点号:

const response = [
  {id: 1, source: 'mobile', 'ms_payment.id': 111, 'ms_payment.total': 100},
  {id: 2, source: 'mobile', 'ms_payment.id': 112, 'ms_payment.total': 210},
  // ...更多项
];

而我的REST API需要返回嵌套结构的数组:

const result = [
  {id: 1, source: 'mobile', ms_payment: { id: 111, total: 100 }},
  {id: 2, source: 'mobile', ms_payment: { id: 112, total: 210 }},
  // ...更多项
];

目前我是用Array.map()手动处理的,但想找更高效的JavaScript算法,求解答。


解答

嘿,这个需求我之前也碰到过!首先得说:你用Array.map()的思路本身没毛病,但确实可以优化得更灵活,或者从源头减少转换成本。我给你分享几个实用的方案:

1. 通用化的map优化版(兼顾效率和灵活性)

如果你还是想在JS层面处理,这个通用版的转换函数比手动逐个赋值更灵活,而且性能和原生map持平:

const flattenToNested = (data) => {
  return data.map(item => {
    const nestedItem = {};
    // 遍历每个键值对
    for (const [key, value] of Object.entries(item)) {
      if (key.includes('.')) {
        // 拆分父键和子键
        const [parentKey, childKey] = key.split('.');
        // 初始化父对象(如果不存在)
        nestedItem[parentKey] = nestedItem[parentKey] || {};
        nestedItem[parentKey][childKey] = value;
      } else {
        // 非嵌套键直接赋值
        nestedItem[key] = value;
      }
    }
    return nestedItem;
  });
};

// 使用示例
const result = flattenToNested(response);

这个方法的时间复杂度是O(n*m)(n是数组长度,m是每个对象的键数量),这已经是理论最优了——毕竟每个键都得处理一次。而且它能适配更多嵌套字段的情况,比如以后加个ms_payment.status也不用改代码。

2. 从Knex查询阶段直接生成嵌套结构(性能最优)

其实最高效的方式是避免在JS层面做转换,让Knex配合数据库直接返回嵌套结构。比如如果用PostgreSQL,可以利用它的JSON函数:

const nestedResults = await knex('your_table_name')
  .select(
    'id',
    'source',
    // 用SQL直接构建嵌套的JSON对象
    knex.raw(`json_build_object(
      'id', ms_payment_id,
      'total', ms_payment_total
    ) AS ms_payment`)
  )
  .then(rows => {
    // 把数据库返回的JSON字符串解析成对象
    return rows.map(row => ({
      ...row,
      ms_payment: JSON.parse(row.ms_payment)
    }));
  });

这样查询出来的结果直接就是你要的嵌套格式,省去了JS层面的循环转换,性能会更好——毕竟数据库处理这类结构化数据的效率通常比JS高。

如果是MySQL,也可以用JSON_OBJECT函数来实现类似效果:

knex.raw(`JSON_OBJECT('id', ms_payment_id, 'total', ms_payment_total) AS ms_payment`)

3. 超大数据量下的reduce方案

如果你的数据量特别大(比如几十万条),reduce和map的性能差异几乎可以忽略,但写法上可以这样:

const flattenToNested = (data) => {
  return data.reduce((acc, item) => {
    const nestedItem = {};
    for (const [key, value] of Object.entries(item)) {
      if (key.includes('.')) {
        const [parentKey, childKey] = key.split('.');
        nestedItem[parentKey] = { ...(nestedItem[parentKey] || {}), [childKey]: value };
      } else {
        nestedItem[key] = value;
      }
    }
    acc.push(nestedItem);
    return acc;
  }, []);
};

不过实际测试中,map和reduce在处理数组时的性能表现几乎一致,除非你在极端场景下,否则没必要特意切换。


总结

  • 如果数据量不大,通用版的map方案完全够用,灵活又好维护;
  • 如果追求极致性能,优先在Knex查询阶段用数据库的JSON函数生成嵌套结构;
  • 手动逐个字段赋值的map(你原来的方法)虽然稍微快一点,但灵活性差,字段变化时要改代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 17:07:31