5

I have a this type of a json object.

{
    "id": "001",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "path": ["001"],
    "children": [
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        },
        {
            "id": "003",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "003"],
            "children": [
                {
                    "id": "00001",
                    "type": "B",
                    "children": []
                }
            ]
        },
        {
            "id": "004",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "004"],
            "children": [
                {
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": []
                },{
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": [
                        {
                            "id": "00002",
                            "type": "B",
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "id": "00003",
            "type": "B",
            "children": []
        }
    ]
}

I need to replace all the object which is type: "B" , with this (which is mentioned below) type of object which I can get from an object with ids as keys of typed B. This typed B objects can be nested anywhere either as a first child or fifth child of a nested arrays of children

{
    "id": "002",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "children": []
},

How can I do that? This can be deeply nested and there's no specific place where we should replace the objects, beforehand. So, I need to go through the entire object and do that. How should I get it done?

EDIT

I updated the code in the question slightly. There's a path property of the nested in each object, except for typed B objects. So, when replacing the typed B properties with the other object, I need to add the paths in there as well.

eg: path for id: "00001", typed B object should be : ["001", "003", "00001"]

EDIT : Expected result

{
    "id": "001",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "path": ["001"],
    "children": [
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        },
        {
            "id": "003",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "003"],
            "children": [
                {
                    "id": "002",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "003", "002"],
                    "children": []
                },
            ]
        },
        {
            "id": "004",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "004"],
            "children": [
                {
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": []
                },{
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": [
                        {
                            "id": "002",
                            "type": "A",
                            "value": "aaaaa",
                            "data:": {},
                            "path": ["001", "004", "005", "002"],
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        }
    ]
}
Thidasa Pankaja
  • 930
  • 8
  • 25
  • 44
  • Likely a recursive function; what's the specific issue? – Dave Newton Jan 12 '22 at 03:47
  • 1
    Does this answer your question? [Replacing a function deeply nested inside an object in javascript](https://stackoverflow.com/questions/59327168/replacing-a-function-deeply-nested-inside-an-object-in-javascript) – Kinglish Jan 12 '22 at 03:51
  • Thank you @Kinglish . But in this case, I don't know where it should be replaced beforehand rather than going inside all the paths in the object. So, I think it's not straightforward as mentioned in that answer. – Thidasa Pankaja Jan 12 '22 at 04:09
  • @DaveNewton Thank you. Yes. I'm implementing a recursive function for this. But since I don't know where to be replaced beforehand, I need to go through all the paths in the object. Was wondering whether there's any other approach or similar scenarios – Thidasa Pankaja Jan 12 '22 at 04:09
  • That's the recursive part. – Dave Newton Jan 12 '22 at 04:27

3 Answers3

7

lodash if you don't mind.

Use cloneDeepWith to clone the entire tree and replace a specific value.

const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};

const result = _.cloneDeepWith(data, (value) => {
  const newObj = {"id": "002", "type": "A", "value": "---NEW VALUE FOR 'B' TYPE---", "data:": {} };
  return (value.type === 'B') ? { ...value, ...newObj} : _.noop();
});


console.dir(result, { depth: null } );
.as-console-wrapper{min-height: 100%!important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js" integrity="sha512-2iwCHjuj+PmdCyvb88rMOch0UcKQxVHi/gsAml1fN3eg82IDaO/cdzzeXX4iF2VzIIes7pODE1/G0ts3QBwslA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

--- Update 2--- (Without lodash)

Use local variable to store and combine current path.

const data = { "id": "001", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001"], "children": [{ "id": "002", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "002"], "children": [] }, { "id": "003", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "003"], "children": [{ "id": "00001", "type": "B", "children": [] }] }, { "id": "004", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004"], "children": [{ "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [] }, { "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [{ "id": "00002", "type": "B", "children": [] }] }] }, { "id": "00003", "type": "B", "children": [] }] }

const deepReplace = (obj, prevPath = []) => {
    if (obj.type === 'A') {
      if  (obj.children.length) {
        obj.children = obj.children.map((childObj) => deepReplace(childObj, obj.path))
      }
      return obj;
    };
    if (obj.type === 'B') {
      const id = '002';
      return { id, type: "A", value: "aaaaa",  path: [...prevPath, id],  data: {}, children: []};
    };
 };

console.dir(deepReplace(data), { depth: null });
.as-console-wrapper{min-height: 100%!important; top: 0}
A1exandr Belan
  • 4,442
  • 3
  • 26
  • 48
2

This looks like a tree traversal problem. Here's a way to handle that with depth-first search without recursion.

As mentioned in this answer, recursion should be avoided whenever possible, as it requires more memory and is more difficult to debug than an iterative implementation.

Updated per adjustment in question

const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"path":["001"],"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"path":["001","002"],"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"path":["001","003"],"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"path":["001","004"],"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};

const dfs = () => {
  const stack = [[data, null]];
  
  while(stack.length) {
    const [curr, parent] = stack.pop();
    // check for match on type
    if (curr.type === "B") {
      curr.type = "A";
      curr.id = "002";
      curr.value = "aaaaa";
      curr.data = {};
      curr.path = [...parent?.path.slice() ?? [], "002"];
    }
    curr.children.forEach(child => stack.push([child, curr]));
  }
};

dfs();

const output = document.getElementById("output");
output.innerText = JSON.stringify(data, null, 2);
<pre id="output" />
GenericUser
  • 3,003
  • 1
  • 11
  • 17
1

I played around with this in the console and it did what you wanted (based on the json array provided, setting all the "B" to "A" types). It's a recursive function so on any nested child it meets in the "children" array it would call the function again on each item in the array.


function fixObjects (obj) {
  if (obj["type"] === "B") {
    obj["type"] = "A";
    obj["id"] = "002";
    obj["value"] = "aaaaa";
    obj["data"] = {};
  } 
  if (obj["children"].length > 0) {
     obj["children"].forEach (child => fixObjects (child));
  }
}

fixObjects (_yourArray)

Jesser
  • 76
  • 7
  • Thank you. this works. However, for type B objects, I need to update a property based on the parent's object as well . That's what I'm trying to figure out now. – Thidasa Pankaja Jan 12 '22 at 07:25