0

I am attempting to build a new object from an existing deep nested object. I can't seem to get my mind in recurive mode but I am running into a bit of trouble:

oldObjArr = [{
  id:1,
  name:"Record1"
},{
  id:2,
  name:"Record2"  
},{
  id:3,
  name:"Record3",
  kids:[{
    id: 4,
    name: "Child 3-1"
  },{
    id: 5,
    name: "Child 3-2"  
  }]
}]

buildTreeNodes = (node) => {
  let data = []
  node.map(record=>{
    record["icon"] = "..."
    record["color"] = "..."
    data.push(record)
    record.kids && buildTreeNodes(record.kids)
  })
} 
let newObjArr = buildTreeNodes(oldObjArr)

This OBVIOUSLY does not work, but I can't figure out what will. The resulting object should look like this:

[{
  id:1,
  name:"Record1",
  icon:"...",
  color: "...",
},{
  id:2,
  name:"Record2",
  icon:"...",
  color: "...",  
},{
  id:3,
  name:"Record3",
  icon:"...",
  color: "...",
  kids:[{
    id: 4,
    name: "Child 3-1",
    icon:"...",
    color: "...",
  },{
    id: 5,
    name: "Child 3-2",
    icon:"...",
    color: "...",
  }]
}]

Thanks for any help.

5
  • did you forget to foreach? Commented May 14, 2021 at 13:34
  • You have got map and forEach mixed up. Map is for mapping into a new array. What your wanting to do is mutate, so forEach without all that push stuff should do it Commented May 14, 2021 at 13:36
  • Actually my first attempt at this was using foreach; however I still got the same incorrect result. Commented May 14, 2021 at 13:36
  • oldObjArr.forEach(buildTreeNodes) and record.kids.forEach(buildTreeNodes) Commented May 14, 2021 at 13:37
  • 1
    Maybe you forgot return node.map(...) Commented May 14, 2021 at 13:38

4 Answers 4

2

Robert's answer is correct.

If by chance you also want to not mutate the original object, then you can do something like this.

Also using ES6 features coz why not.

const oldObjArr = [{
  id: 1,
  name: "Record1"
}, {
  id: 2,
  name: "Record2"
}, {
  id: 3,
  name: "Record3",
  kids: [{
    id: 4,
    name: "Child 3-1"
  }, {
    id: 5,
    name: "Child 3-2"
  }]
}];

function transformObject(item) {
    if (Array.isArray(item.kids))
        return { 
            ...item, icon: '...', color: '...',
            kids: item.kids.map(transformObject)
        };
    else
        return {...item, icon: '...', color: '...' };
}

const newArray = oldObjArr.map(transformObject);
console.log(newArray);

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

2 Comments

I like your solution. Nice. :) Immutable. I thought on that, but decide it can be too complicated for beginner struggle with this kind of basic problem.
Glad to help :). We all are beginners once.
1

So you iterate over you array and take each object and then add your props to it. Then you check if kids exist and some check if is array. i use instanceof but like @Heretic Monkey point it can be Array.isArray. What more you can setup type guard on front of function check that array argument is array then this you don't have to check that if kids is type of array.

const oldObjArr = [{
  id:1,
  name:"Record1"
},{
  id:2,
  name:"Record2"  
},{
  id:3,
  name:"Record3",
  kids:[{
    id: 4,
    name: "Child 3-1"
  },{
    id: 5,
    name: "Child 3-2"  
  }]
}]

const addKeys = arr => {
  for(const obj of arr){
    obj['icon'] = "test"
    obj['color'] = "test"
    if("kids" in obj && obj.kids instanceof Array){
      addKeys(obj.kids);
    }
  }
}
  
addKeys(oldObjArr)
console.log(oldObjArr)

V2

const addKeys = arr => {
  if(!Array.isArray(arr))
    return;
  for(const obj of arr){
    if(typeof obj !== "object")
      continue;
    obj['icon'] = "test"
    obj['color'] = "test"
    if("kids" in obj){
      addKeys(obj.kids);
    }
  }
}

6 Comments

I'd just use Array.isArray(obj.kids); if kids doesn't exist, it'll be undefined, which is not an array.
Also, an explanation of code is always welcome on Stack Overflow.
@HereticMonkey Sadly on stack often first answer is best one (even it is not) so i have strategy: post code first then edit description.
Well, it's a good way to attract downvotes from people like me who sees a dump of code with no explanation and thinks, "that's not useful". YMMV.
I understand your point. But usually i made description before first comment. Thanks for your suggestion.
|
0

Ok check this out:

buildTreeNodes = (node) => {
  let data = node.map(record=>{
    record["icon"] = "..."
    record["color"] = "..."
    if (record.kids) record.kids = buildTreeNodes(record.kids);
    return record;
  })
  return data;
} 
let newObjArr = buildTreeNodes(oldObjArr)
console.log(newObjArr)

I think this is what you were after. You have to return record with each iteration of map, and it will add it directly to data array. The recursion within works the same.

Comments

0

All details are commented in demo below

let objArr = [{
    id: 1,
    name: "Record 1"
  }, {
    id: 2,
    name: "Record 2"
  }, {
    id: 3,
    name: "Record 3",
    kids: [{
      id: 4,
      name: "Child 3-1"
    }, {
      id: 5,
      name: "Child 3-2"
    }]
  },
  /*
  An object with a nested object not in an array
  */
  {
    id: 6,
    name: 'Record 6',
    kid: {
      id: 7,
      name: 'Child 6-1'
    }
  },
  /*
  An object that's filtered out because it doesn't have 'id' key/property
  */
  {
    no: 0,
    name: null
  },
  /*
  An object that's filtered out because it doesn't have 'id' key/property BUT has a nested object that has 'id'
  */
  {
    no: 99,
    name: 'Member 99',
    kid: {
      id: 8,
      name: 'Scion 99-1'
    }
  }
];

/*
Pass an object that has the key/value pairs that you want added to other objects
*/
const props = {
  icon: '...',
  color: '...'
};

/*
Pass...
a single object: {obj} of objArr[] 
a single key/property: 'id'
an object that contains the key/value pairs to be added to each object that has key/property of id: {props}
*/
const addProp = (obj, prop, keyVal) => {
  /* 
  Convert {props} object into a 2D array
  props = {icon: '...', color: '...'}
  ~TO~
  kvArr = [['icon', '...'], ['color', '...']]
  */
  let kvArr = Object.entries(keyVal);

  /*
  for Each key/value pair of kvArr[][]
  assign them to the (obj} if it has ['prop'] 
  as one of it's key/properties
  (in this demo it's 'id')
  */
  kvArr.forEach(([key, val]) => {
    if (obj[prop]) {
      obj[key] = val;
    }
  });

  /*
  Convert {obj} into a 2D array
  obj = {id: 3, name: "Record 3", kids: [{   id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]}
  ~TO~
  subArr = [['id', 3], ['name', "Record 3"], ['kids', [{id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]] 
*/
  let subArr = Object.entries(obj);

  /*
  for Each value of subArr[][] (ie ['v'])
  if it's an [Array] call addProp and pass 
  the {obj} of subArr[][]
  */
  /* 
  if it's an {obj} do the same as above
  */
  subArr.forEach(([k, v]) => {
    if (Array.isArray(v)) {
      v.forEach(subObj => {
        addProp(subObj, prop, keyVal);
      });
    } else if (v instanceof Object) {
      addProp(v, prop, keyVal);
    }
  });
};

// Run addProp() on each {obj} of objArr[]
for (let object of objArr) {
  addProp(object, 'id', props);
}

console.log(JSON.stringify(objArr, null, 2));

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.