【问题标题】:How to group and concat values by keys in JS如何在 JS 中通过键对值进行分组和连接
【发布时间】:2021-12-17 17:52:29
【问题描述】:

我尝试像疯子一样对对象数组进行某种合并/按键排序。 不知道为什么,但我不知道该怎么做。

我的应用在 Ionic / Angular 中。

这是我所拥有的:

[
  {
      "2021": {
          "a": "aText"
      }
  },
  {
      "2021": {
          "b": "bText"
      }
  },
  {
      "2020": {
          "z": "zText"
      }
  },
  {
      "2020": {
          "y": "yText"
      }
  },
  {
      "2020": {
          "x": "xText"
      }
  }
]

我的目标是得到这个:

[
  {
      "2021": { "a": "aText", "b": "bText" }
  },
  {
      "2020": { "z": "zText", "y": "yText", "x": "xText" }
  }
]

换句话说,我想按年份重新组合并连接它们。

有人知道怎么做吗?

【问题讨论】:

  • 为什么不使用以年份为键的对象而不是对象数组呢?可能更容易管理。
  • 这是我尝试过的另一种选择,但一切都是动态的,有点搞砸了我的想法......
  • 您应该将您尝试过的代码作为minimal reproducible example 添加到您的问题中。
  • 我尝试了很多可能的方法,以至于我什至不知道该向您展示什么。结果总是一样的,我无法操纵返回的数据并对它们进行简单的连接......头脑要爆炸了:)
  • 安迪,你的第一条评论让我想到了一些事情。您对以年为键的对象是正确的。当我这样做时的问题是我如何检查密钥是否在对象中多次(如果是,我可以做连接)。不知道我说的够不够清楚。

标签: javascript typescript


【解决方案1】:

如果您只想要一个以年份作为键的对象,那么它只是一个标准的“分组依据”,带有一个嵌套循环来迭代每个对象的 Object.entries()。如果您确实想要最初发布的输出(单个对象的数组),您可以 map() 返回的分组对象的全部内容并使用 Object.fromEntries() 将每个对象转换为一个对象

const input = [
  { 2021: { a: 'aText' } },
  { 2021: { b: 'bText' } },
  { 2020: { z: 'zText' } },
  { 2020: { y: 'yText' } },
  { 2020: { x: 'xText' } },
];

const grouped_object = input.reduce(
  (a, o) => (Object.entries(o).forEach(([y, o]) => (a[y] = { ...(a[y] ?? {}), ...o })), a),
  {}
);

// if you just want a single object with years as keys
console.log(grouped_object);

const grouped_array = Object.entries(grouped_object)
  .map(([year, data]) => ({[year]: data}));

// the output from your question
console.log(grouped_array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

或重构为使用for...of 循环和Object.assign()

const input = [
  { 2021: { a: 'aText' } },
  { 2021: { b: 'bText' } },
  { 2020: { z: 'zText' } },
  { 2020: { y: 'yText' } },
  { 2020: { x: 'xText' } },
];

const grouped_object = {};
for (const obj of input) {
  for (const [year, data] of Object.entries(obj)) {
    grouped_object[year] = Object.assign(grouped_object[year] ?? {}, data);
  }
}

// if you just want a single object with years as keys
console.log(grouped_object);

// or avoiding computed properties
const grouped_array = Object.entries(grouped_object)
  .map(([year, data]) => (o={}, o[year]=data, o));

// the output from your question
console.log(grouped_array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 谢谢 Pilchard,但当我尝试您的第一个命题时出现错误:“类型 'ObjectConstructor' 上不存在属性 'fromEntries'。您需要更改目标库吗?尝试更改 'lib ' 'es2019' 或更高版本的编译器选项。"
  • Object.fromEntries() 调用替换为使用计算属性的对象文字声明。听起来您正在编译到较旧的规范,在这种情况下使用上面的 Object.assign 示例,因为扩展语法可能也不支持。
  • Pilchard,我已将 tsconfig.json "lib": ["es2018", "dom"] 替换为 "lib": ["es2019", "dom"] 问题是加上我现在得到了我想要的结果。非常感谢先生。
  • 不用担心,很高兴它有帮助。
【解决方案2】:

最好使用对象按年份分组。这是一个使用reduce 遍历数组以生成该对象的示例。

const data=[{2021:{a:"aText"}},{2021:{b:"bText"}},{2020:{z:"zText"}},{2020:{y:"yText"}},{2020:{x:"xText"}}];

const out = data.reduce((acc, obj) => {

  // Get the key and value from the object that in
  // the current iteration
  const [ [ key, value ] ] = Object.entries(obj);

  // If the key doesn't exist on the accumulator (the initial
  // object that we passed into the `reduce`) create an empty object
  acc[key] = acc[key] || {};

  // Update the value of that object property with
  // the value of the object
  acc[key] = { ...acc[key], ...value };

  // Return the updated object for the next iteration
  return acc;

// Here's the initial object that
// acts as the accumulator through all the iterations
}, {});

console.log(out);

或者使用数组来保存每年的信息:

const data=[{2021:{a:"aText"}},{2021:{b:"bText"}},{2020:{z:"zText"}},{2020:{y:"yText"}},{2020:{x:"xText"}}];

const out = data.reduce((acc, obj) => {

  const [ [ key, value ] ] = Object.entries(obj);

  // Use an array instead of an object
  acc[key] = acc[key] || [];

  // Push the first element of the Object.values
  // into the array
  acc[key] = [ ...acc[key], Object.values(value)[0] ];

  return acc;

}, {});

console.log(out);

其他文档

【讨论】:

  • 非常感谢 Andy,但我收到此错误消息:“...值”上的“传播类型只能从对象类型创建”。有什么想法吗?
  • Sounds like a TS warning.。正如您在示例中看到的那样,JS 工作正常。
  • 安迪,你是对的,这是一个警告。在 tsconfig.json "lib": ["es2018", "dom"] 更改为 "lib": ["es2019", "dom"] 后一切正常(希望它不会破坏我的应用程序的其余部分)。谢谢你的帮助。 Prichard 的代码起到了作用,但你的代码也帮助了我很多。对于您的帮助,我不能再给您更多的尊重。
猜你喜欢
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
  • 2015-11-04
  • 2020-10-21
  • 2022-11-14
  • 1970-01-01
  • 1970-01-01
  • 2017-05-13
相关资源
最近更新 更多