2

I have a nested object with no pre-determinable path to child objects - example:

{
    children : 
    [
        {
            children : 
            [
                {
                    children : 
                    [
                        {
                            id : "F100517B-D00F",
                            level : 4,
                            note : "update me",
                            parentId : "23A2A0DB-CCE3",
                            title : "change me"
                        }
                    ],
                    id : "23A2A0DB-CCE3",
                    level : 3,
                    note : "me too",
                    parentId : "a0H4H00000Roi",
                    title : "..and me"
                }
            ],
            id : "a0H4H00000Roi",
            level : 2,
            note : "none",
            parentId : "0064H00000ysL",
            title : "pending"
        },
        {
            "children" : 
            [
                {
                    id : "6A45E2EC-7825",
                    level : 3,
                    note : "|",
                    parentId : "a0H4H00000Roi",
                    title : ""
                }
            ],
            id : "a0H4H00000Roi",
            level : 2,
            note : "",
            parentId : "0064H00000ysL",
            title : "Change me"
        }
    ],
    id : "0064H00000ysL",
    level : 1,
    note : "hello",
    title : "Test Co"
}

Following this I generate a list of updates via a map function - sample results:


    [{
      content: "New Co",
      id: "0064H00000ysL",
      note: "Here's your update"
    }, {
      content: "91%",
      id: "a0H4H00000Roi",
      note: "New note here"
    }]

I need to iterate through the update object array and update nested object values, I've tried a few things but can't seem to quite nail it (my JS skill is a bit limited atm).

Here's my last attempt, taken from the closest solution I found here: Javascript update values in nested object by array path

    var updates = $('div.node').map(function() {
    return {
              id: $(this).attr("id"),
              content: $(this).find('div.content').text(),
              note: $(this).find('div.note').text()
           };
       }).get();

const checkAndChange = (obj, update) => { //function to check id match for update
  if (update.id.includes(obj.id)) {
    obj.title = update.content;
    obj.note = update.note;
  }
}

const recursion = (obj, update) => {
   const o = obj;
   checkAndChange(o, update); // check if id exists update values
   if (o.children.length > 0) { //check if has children
        o.children.forEach(v => { //if has children do same recursion for children
        recursion(v, update);
      });
   }
   return o; //return updated object
}
var updatesLength = updates.length;
for(let it = 0; it < updatesLength; it++) {
    recursion(obj, updates[it]);
}

console.log(obj)

The indented map function at the top works fine but I get Uncaught TypeError: Cannot read properties of undefined (reading 'id')" when I try to loop though the update array and write back to the main object (obj).

Any help appreciated

4
  • You need to make sure you are passing update when you call checkAndChange and recursion. Commented Oct 7, 2021 at 23:42
  • Ah yes, note to tired eyes, don't post help requests near midnight. Wait until the morning and review with fresh eyes. I've corrected the code above but still get: "Cannot read properties of undefined (reading 'length')" Commented Oct 8, 2021 at 11:02
  • You need to check o.children exists before checking its length. But anyway, see Terry's answer. Commented Oct 8, 2021 at 11:52
  • Thanks for your input @MikeM - appreciated Commented Oct 8, 2021 at 17:01

1 Answer 1

3

You can use a redursive approach here, we'll create a function updateNestedObj() to apply the updates, applying to each object and any of its child objects:

const objToUpdate = { children : [ { children : [ { children : [ { id : "F100517B-D00F", level : 4, note : "update me", parentId : "23A2A0DB-CCE3", title : "change me" } ], id : "23A2A0DB-CCE3", level : 3, note : "me too", parentId : "a0H4H00000Roi", title : "..and me" } ], id : "a0H4H00000Roi", level : 2, note : "none", parentId : "0064H00000ysL", title : "pending" }, { "children" : [ { id : "6A45E2EC-7825", level : 3, note : "|", parentId : "a0H4H00000Roi", title : "" } ], id : "a0H4H00000Roi", level : 2, note : "", parentId : "0064H00000ysL", title : "Change me" } ], id : "0064H00000ysL", level : 1, note : "hello", title : "Test Co" } 

const updateArr = [{ content: "New Co", id: "0064H00000ysL", note: "Here's your update" }, { content: "91%", id: "a0H4H00000Roi", note: "New note here" }];

function updateNestedObj(obj, updates) {
    const updateToApply = updates.find(upd => upd.id === obj.id);
    if (updateToApply) {
        obj.title = updateToApply.content;
        obj.note = updateToApply.note;
    }
    // Apply updates to any child objects
    for(let k in obj) {
        if (typeof(obj[k]) === 'object') {
            updateNestedObj(obj[k], updates);
        }
    }
}

updateNestedObj(objToUpdate, updateArr);

console.log(objToUpdate)
        
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

1 Comment

Wonderful, more concise and works like a charm. @Terry thank you for your input, very much appreciated. I need to get my JS skills up to a working level quickly as we'll be using it much more now.

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.