如何动态改变JS对象结构?

有一个JS对象是这样的:

{
  list: [
    {
      firstP0: 1,
      firstP1: 3,
      firstP2: 5,
      firstPxxx: xxx,
      secondP0: 2,
      secondP1: 4,
      secondP2: 6,
      secondPxxx: xxx,
    }
  ]
}

没办法知道具体的键名,但知道其规律是/firstP\d+/或者/secondP\d+/,如何把它变成这样的结构:

{
  list: [
    {
      first: {
        P0: 1,
        P1: 3,
        P2: 5,
        Pxxx: xxx,
      },
      second: {
        P0: 2,
        P1: 4,
        P2: 6,
        Pxxx: xxx,
      }
    }
  ]
}
阅读 2.3k
2 个回答

image.png

obj.list.map(item => {
    return Object.entries(item).reduce((acc, [key, val]) => {
            var regObj = {
                'first': /firstP\d+/,
                'second': /secondP\d+/,
            }
            Object.entries(regObj).forEach(([name, reg]) => {
                    if (new RegExp(reg).test(key)) {
                        var suffix = key.replace(new RegExp('^' + name), '');
                        (acc[name] ||={})[suffix] = val;
                    }
                })
            return acc;
        }, {})
})

简写如下
image.png

var regObj = {
                'first': /firstP\d+/,
                'second': /secondP\d+/,
            }
obj.list.map(item => Object.entries(item).reduce((acc, [key, val]) =>(Object.entries(regObj).forEach(([name, reg]) => new RegExp(reg).test(key) && ((acc[name] ||={})[key.replace(new RegExp('^' + name), '')] = val)),acc), {}))

思路:

  1. 用正则表达式把 "firstP01" 拆分成 "first""P01"
  2. 用 groupBy 把对象属性按拆分后的前面部分分组

由于原生 JS 数组不提供分组,干脆使用 Lodash

import _ from "lodash";

const data = {
    list: [
        {
            firstP0: 1,
            firstP1: 3,
            firstP2: 5,
            firstP3: "xxx",
            secondP0: 2,
            secondP1: 4,
            secondP2: 6,
            secondP3: "xxx",
        }
    ]
};

function transform(obj) {
    return _(obj).entries()
        .map(([key, value]) => {
            console.log(key, value);
            const [, group, k] = key.match(/^(.*)?(P\d+)$/);
            return { group, key: k, value };
        })
        .groupBy("group")
        .mapValues(arr => Object.fromEntries(
            arr.map(({ key, value }) => [key, value])
        ))
        .value();
}

const r = data.list.map(transform);
console.dir(r);

输出

[
  {
    first: { P0: 1, P1: 3, P2: 5, P3: 'xxx' },
    second: { P0: 2, P1: 4, P2: 6, P3: 'xxx' }
  }
]

如果不想用 Lodash,也可以直接 reduce 来实现

function transform(obj) {
    return Object.entries(obj)
        .map(([key, value]) => {
            const [, group, k] = key.match(/^(.*)?(P\d+)$/);
            return { group, key: k, value };
        })
        .reduce((target, { group, key, value }) => {
            const g = (target[group] ??= {});
            g[key] = value;
            return target;
        }, {});
}

实际测试了一下,不用 Lodash,使用原生方法要快得多,毕竟处理过程要简单些。

推荐问题