0

I would like to restructure a pain object to a 2D array object.

Originally, I have something like:

{ 
   "education_histories.0.school":[ 
      "The education_histories.0.school field is required."
   ],
   "education_histories.0.degree":[ 
      "The education_histories.0.degree field is required."
   ],
   "education_histories.1.school":[ 
      "The education_histories.1.school field is required."
   ],
   "education_histories.1.degree":[ 
      "The education_histories.1.degree field is required."
   ],
}

I would like to restructure it to be like:

[ 
   { 
      "school":[ 
         "The education_histories.0.school field is required."
      ],
      "degree":[ 
         "The education_histories.0.degree field is required."
      ]
   },
   { 
      "school":[ 
         "The education_histories.1.school field is required."
      ],
      "degree":[ 
         "The education_histories.1.degree field is required."
      ]
   }
]

Currently, I've been trying to do something like:

let validationErrors = []    

$.each(this.errors, (key, value) => {
  let splitKey = key.split('.')

  validationErrors[splitKey[1]] = { [splitKey[2]]: value }
})

Of course, this won't work because It keeps overriding until the last round. The output would be like:

    [ 
       {
          "degree":[ 
             "The education_histories.0.degree field is required."
          ]
       },
       {
          "degree":[ 
             "The education_histories.1.degree field is required."
          ]
       }
    ]

I wish I could do something like

let validationErrors = []    

$.each(this.errors, (key, value) => {
  let splitKey = key.split('.')

  validationErrors[splitKey[1]][splitKey[2]] = value
})

But this won't work either. It says "TypeError: Cannot set property 'school' of undefined"

Any help would be really appreciated.

0

3 Answers 3

2

You don't have to change much. If the object doesn't exist, create it, otherwise just set its property. You can also use object destructuring and there's no need for jQuery:

  let validationErrors = []    

 for(const [key, value] of Object.entries(this.errors)) {
    let [ , index, name] = key.split('.');

    if(!validationErrors[index]) valudationErrors[index] = {};
    validationErrors[index][name] = value;
}
Sign up to request clarification or add additional context in comments.

Comments

0

Let's try my way, I think it is not best solution:

const input = {
  "education_histories.0.school": [
    "The education_histories.0.school field is required."
  ],
  "education_histories.0.degree": [
    "The education_histories.0.degree field is required."
  ],
  "education_histories.1.school": [
    "The education_histories.1.school field is required."
  ],
  "education_histories.1.degree": [
    "The education_histories.1.degree field is required."
  ],
};

const raw = Object
  .keys(input) // ["education_histories.0.school", "education_histories.0.degree"]
  .reduce((pre, curr) => {
    const splittedStr = curr.split('.');
    const dataKey = splittedStr.pop(); // school
    const key = splittedStr.join('.'); // education_histories.0
    if (!pre[key]) {
      pre[key] = {};
    }
    pre[key][dataKey] = input[curr];
    return pre;
  }, {});

console.log(raw);

// {
//   "education_histories.0": {
//     "school": [
//       "The education_histories.0.school field is required."
//     ],
//       "degree": [
//         "The education_histories.0.degree field is required."
//       ]
//   },
//   "education_histories.1": {
//     "school": [
//       "The education_histories.1.school field is required."
//     ],
//       "degree": [
//         "The education_histories.1.degree field is required."
//       ]
//   }
// }

const output = Object.keys(raw).map((key) => raw[key]);

console.log(output);

Comments

0

Object.entries and Array.reduce are good for doing this sort of thing.

Suggested solution here, overly verbose explanation below.

const input = {
  "education_histories.0.school": [
    "The education_histories.0.school field is required."
  ],
  "education_histories.0.degree": [
    "The education_histories.0.degree field is required."
  ],
  "education_histories.1.school": [
    "The education_histories.1.school field is required."
  ],
  "education_histories.1.degree": [
    "The education_histories.1.degree field is required."
  ],
}

// iterate over each key/value in the input object
// to build up the output.
const result = Object.entries(input).reduce((acc, [key, value]) => {

  // split the key into parts
  // e.g. ['educaton_histories', '0', 'school']
  const [section, index, field] = key.split('.');

  // use the index to bin the message in the right slot
  // e.g. result.0 = { ... }
  acc[index] = {

    // keep anything that already exists in this bin
    ...(acc[index] || {}),

    // add the message under the field's name
    // e.g. school: [ ... ]
    [field]: value
  }

  // return the updated object
  return acc;
}, {})

// the object's values are in the format you're after.
// we don't need the keys (the indices)
console.log(Object.values(result));

Object.entries(input) returns an array of field/value pairs. So:

const input = {
  'education_histories.0.school': 'school 0',
  'education_histories.0.degree': 'degree 0',
  'education_histories.1.school': 'school 1',
  'education_histories.1.degree': 'degree 1',
};

Object.entries(input);

Returns this array of arrays:

[
  ["education_histories.0.school", "school 0"],
  ["education_histories.0.degree", "degree 0"],
  ["education_histories.1.school", "school 1"],
  ["education_histories.1.degree", "degree 1"],
]

Invoking reduce on this array calls your callback function for each item in the array, passing in the previous iteration result, and the current array entry:

// reduce takes two args: a callback function and an initial value
Object.entries(input).reduce(callback, {})

// the callback function takes:
// 1) the result from the previous iteration, and
// 2) and the current array item:
function callback(result, currentItem) {
  // on the first iteration:
  // previousResult = {}
  // currentItem[0] === "education_histories.0.school"
  // currentItem[1] === "school 0"

  // incorporate the item into the result:

  // break the key on '.', and capture the three entries:
  // section === 'eduction_histories'
  // index === 0
  // field === 'school 0'
  const [section, index, field] = currentItem[0].split('.');

  // result.0
  result[index] = {
    // if result.0 already exists, keep what's there
    ...(result[index] || {}),

    // add the current item
    // school: 'school 0'
    [field]: currentItem[1]
  }

  // return the updated result.
  // this will be passed in for the next iteration,
  // and will be the final return value when reduce exits
  return result;
}

The callback can be inlined, giving you the example above.

Object.entries(input).reduce((acc, [key, value]) => {
  // ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.