Home > other >  How can I push objects to arrays with similar names?
How can I push objects to arrays with similar names?

Time:04-23

I have a form that I can create duplicate sections. Right now when the form is submitted the form spits out one giant object. I'm working on a filter function that can filter out the duplicate sections and organize the duplicates in array so it works with my API.

e.g.

// base object

  {
    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"
    ...
  }

// what I want is

{
  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"}
  ]

   ...
 }

So far I've been able to filter out the copies from the original object and create the arrays for the copies

 // 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`] = [];
  }
});

Where I'm stuck is it's not clear how to match the object to the appropriate array and push it. e.g.

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

So far I've tried to slice the last three keys of the copy_0 object and match the key to the array name by using array.filter, but that didn't work as expected.

How can I match the 'copy_n' objects to the appropriate array and push those objects to the array?

CodePudding user response:

This might be a bit old fashioned, but using a for...in loop in combination with regex pattern matching against the key might be the clearest solution.

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);

The pattern /^(.*)_copy_(\d )$/ matches anything that ends with _copy_ followed by 1 or more decimals. Everything before _copy_ is placed in capture group 1, the decimals behind _copy_ are placed in capture group 2.

If there is no match (else scenario) we simply assign the value to the same key.

If there is a match (if scenario) we first determine the collection key (copies_key) and the index to use. We then check if result[copies_key] is already set (we check if it's a truthy value to be precise). If it's not, we assign it to an empty array. After that we use the index to assign an object to the correct index in the array.

CodePudding user response:

You can reduce to object's entries to a new object. For each key find if it has a base key (the one before "copy") using a RegExp with lookahead. If it doesn't has a base key, add it directly to the accumulator. If it does, add it to the appropriate "copies" array (initialize it if needed using Logical nullish assignment ??=) (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)

  • Related