如何将对象推送到具有相似名称的数组?

How can I push objects to arrays with similar names?

我有一个可以创建重复部分的表格。现在,当提交表单时,表单会吐出一个巨大的对象。我正在开发一个过滤功能,它可以过滤掉重复的部分并将重复的部分组织成数组,以便它与我的 API.

一起使用

例如

// 基础对象

  {
    question_10: ""
    question_10a: ""
    question_11_checkbox: false
    question_11_checkbox_copy_0: false
    question_11_checkbox_copy_1: true
    question_11_text: "110 Monroe St"
    question_11_text_copy_0: "186 Aspen Road"
    question_12_checkbox: false
    question_12_checkbox_copy_0: false
    question_12_text: "New York"
    question_12_text_copy_0: "South Orange"
    ...
  }

//我要的是

{
  question_10: ""
  question_10a: ""
  question_11_checkbox: false
  question_11_checkbox_copies: [
    { question_11_checkbox_copy_0: false } 
    { question_11_checkbox_copy_1: true }
  ]
  question_11_text: "101 Monroe St"
  question_11_text_copies: [
    { question_11_text_copy_0: "186 Aspen Road"}
  ]
  question_12_checkbox: false
  question_12_checkbox_copies: [
    { question_12_checkbox_copy_0: false}
  ]
  question_12_text: "New York"
  question_12_text_copies: [
    { question_12_text_copy_0: "South Orange"}
  ]

   ...
 }

到目前为止,我已经能够从原始对象中过滤掉副本并为副本创建数组

 // filter out copy keys
const copiesKey = Object.keys(data).filter(key => key.includes('copy'));

const copy = {};

// create arrays for copies
copiesKey.map(copiedQuestion => {

  if (!(`${copiedQuestion.slice(0, copiedQuestion.length - 7)}_copies` in copy)) {
    copy[`${copiedQuestion.slice(0, copiedQuestion.length - 7)}_copies`] = [];
  }
});

我卡住的地方是不清楚如何将对象匹配到适当的数组并将其推送。 例如

question_11_text_copies: [
  { question_11_text_copy_0: "186 Aspen Road" }
]

到目前为止,我已经尝试对 copy_0 对象的最后三个键进行切片,并使用 array.filter 将键与数组名称匹配,但没有按预期工作。

如何将 'copy_n' 个对象匹配到适当的数组并将这些对象推送到数组?

您可以将对象的条目减少到新对象。对于每个键,使用带前瞻性的 RegExp 查找它是否具有基本键(“copy”之前的键)。如果它没有基本密钥,则将其直接添加到累加器中。如果是,将它添加到适当的“副本”数组(如果需要使用逻辑空赋值 ??= 初始化它)(TS playground)。

const fn = obj => Object.entries(obj)
  .reduce((acc, [k, v]) => {
    const [baseKey] = k.match(/.*(?=_copy_)/) ?? [] // get the key from an item with copy
  
    if(!baseKey) acc[k] = v // if no baseKey add the item to the accumulator
    else {
      const copiesKey = `${baseKey}_copies`
      acc[copiesKey] ??= [] // if no copies key add a new one to the accumulator    
      acc[copiesKey].push({ [k]: v }) // create an object and push to the current copies key
    }
    
    return acc
  }, {})

const obj = {"question_10":"","question_10a":"","question_11_checkbox":false,"question_11_checkbox_copy_0":false,"question_11_checkbox_copy_1":true,"question_11_text":"110 Monroe St","question_11_text_copy_0":"186 Aspen Road","question_12_checkbox":false,"question_12_checkbox_copy_0":false,"question_12_text":"New York","question_12_text_copy_0":"South Orange"}
const result = fn(obj)

console.log(result)

这可能有点过时,但使用 for...in 循环结合针对密钥的正则表达式模式匹配可能是最清晰的解决方案。

const data = {
  question_10: "",
  question_10a: "",
  question_11_checkbox: false,
  question_11_checkbox_copy_0: false,
  question_11_checkbox_copy_1: true,
  question_11_text: "110 Monroe St",
  question_11_text_copy_0: "186 Aspen Road",
  question_12_checkbox: false,
  question_12_checkbox_copy_0: false,
  question_12_text: "New York",
  question_12_text_copy_0: "South Orange",
};

const copy_n = /^(.*)_copy_(\d+)$/;
const result = {};
for (const key in data) {
  const value = data[key];
  const match = key.match(copy_n);
  
  if (match) {
    const copies_key = `${match[1]}_copies`;
    const index = parseInt(match[2], 10);
    result[copies_key] ||= [];
    result[copies_key][index] = { [key]: value };
  } else {
    result[key] = value;
  }
}

console.log(result);

模式 /^(.*)_copy_(\d+)$/ 匹配任何以 _copy_ 结尾且后跟一位或多位小数的内容。 _copy_ 之前的所有内容都放在捕获组 1 中,_copy_ 后面的小数部分放在捕获组 2 中。

如果没有匹配项(else 场景)我们只需将值分配给相同的键。

如果匹配(if 场景)我们首先确定集合键(copies_key)和要使用的索引。然后我们检查 result[copies_key] 是否已经设置(我们检查它是否是一个精确的真实值)。如果不是,我们将其分配给一个空数组。之后我们使用 index 将对象分配给数组中的正确索引。