2

I have an object like this:

myObj = {
    prop1: 'prop1_value',
    prop2: 'prop2_value',
    subObj: {
        subProp1: 'subProp1_value',
        subProp2: 'subProp2_value',
        subProp3: 'subProp3_value',
        subprop4: 'subProp4_value',
    },
};

and an array like this:

myArr = [
    'arrayVal_1',
    'arrayVal_2',
    'arrayVal_3',
    'arrayVal_4',
];

For context, subObj and myArr always have the same length.

What I am trying to do is map each of the values from myArr onto the values of subObj.

myObj would look like this when done:

myObj = {
    prop1: 'prop1_value',
    prop2: 'prop2_value',
    subObj: {
        subProp1: 'arrayVal_1',
        subProp2: 'arrayVal_2',
        subProp3: 'arrayVal_3',
        subprop4: 'arrayVal_4',
    },
};

I could manually assign the values to each key individually, but that just seems sloppy. I've tried looping with Object.keys, Object.entries, and Object.values on myObj but just can't seem to reason through this one. Thanks.

dblinkhorn
  • 41
  • 7
  • How do you define which sub property gets which array value, when the order of properties in an object is something you should not rely on? – trincot Apr 09 '22 at 07:15
  • I didn't really even consider that, but that's an issue. I do need the array values and the key values to match up in that specific order. – dblinkhorn Apr 09 '22 at 07:18
  • 2
    That means there is a flaw in your original data structure. When data needs to maintain a certain order, you shouldn't use a plain (sub)object, but an array (of pairs or anything that stores both the property and the value). Object keys can get a surprising order when they are inserted in a different sequence, or when one of them is an integer, ...etc. It is a confusing thing and you'd not be the first to be faced with "unexplainable" bugs because of this. – trincot Apr 09 '22 at 07:19
  • I will have to think about this. The way I am using the subObj elsewhere in the code is to two-way bind the values with radio inputs whose values are strings. Because the radio groups are being dynamically created, I needed a map in order to keep the bindings separated from one another. Thanks for the heads up on this issue. – dblinkhorn Apr 09 '22 at 07:23
  • 2
    @trincot Although since ES2015 object key ordering is predicable. – Keith Apr 09 '22 at 07:27
  • @Keith, predictable but not trivial, and bad practice to rely on -- which is my point. Also, since ES2015 there are still ambiguities that only got resolved in later versions, and [only in ES2020](https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties/60121411#60121411) it got completely settled – trincot Apr 09 '22 at 07:34
  • Furthermore, it is impossible to achieve that a key "4" appears *after* a key "A". – trincot Apr 09 '22 at 07:40
  • 1
    @trincot Had a discussion like this ages ago, and all those edge cases were surfaced, but looking at the keys the Op has used here, it should be fine. Not saying its ideal, but the Op having to restructure all his code might be overkill.. – Keith Apr 09 '22 at 07:46

3 Answers3

2

You could do it with Object.fromEntries and Object.keys this way:

myObj.subObj = Object.fromEntries(Object.keys(myObj.subObj).map((k,i) => [k, myArr[i]]))

Be aware there are currently no safe-guards of any kind, so you might want to check if the two datastructures fit, before doing this.

If you have something that you can order by object's keys by, you can also use sort to ensure you assign the correct values to the correct keys, because the resulting order of Object.keys is not guaranteed

 myObj.subObj = Object.fromEntries(Object.keys(myObj.subObj)
  .sort((a,b) => a.localeCompare(b)) //sort keys ascending by name 
  .map((k,i) => [k, myArr[i]]))
derpirscher
  • 14,418
  • 3
  • 18
  • 35
2

Using Object#keys and Array#forEach:

const 
  myObj = {
    prop1: 'prop1_value',
    prop2: 'prop2_value',
    subObj: { subProp1: 'subProp1_value', subProp2: 'subProp2_value', subProp3: 'subProp3_value', subprop4: 'subProp4_value' }
  },
  myArr = [ 'arrayVal_1', 'arrayVal_2', 'arrayVal_3', 'arrayVal_4' ];
  
const { subObj } = myObj;
Object.keys(subObj).forEach((prop, index) => { subObj[prop] = myArr[index] });

console.log(myObj);
Majed Badawi
  • 27,616
  • 4
  • 25
  • 48
1

You want to loop your keys and array together. One way is to try like this:

const myObj = {
    prop1: 'prop1_value',
    prop2: 'prop2_value',
    subObj: {
        subProp1: 'subProp1_value',
        subProp2: 'subProp2_value',
        subProp3: 'subProp3_value',
        subprop4: 'subProp4_value',
    },
};

const myArr = ['arrayVal_1', 'arrayVal_2', 'arrayVal_3', 'arrayVal_4'];

const keys = Object.keys(myObj.subObj); // You should actually sort these according to myArr to ensure safety

for (let i = 0; i < myArr.length; i++) {
    myObj.subObj[keys[i]] = myArr[i];
}

console.log(myObj);
Devesh
  • 603
  • 2
  • 11