Elcid_91
Elcid_91

Reputation: 1681

Javascript building a nested object from a nested object

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.

Upvotes: 0

Views: 78

Answers (4)

zer00ne
zer00ne

Reputation: 43880

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));

Upvotes: 0

Robert
Robert

Reputation: 2753

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);
    }
  }
}

Upvotes: 1

Nisanth Reddy
Nisanth Reddy

Reputation: 6405

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);

Upvotes: 2

Nick Carbone
Nick Carbone

Reputation: 218

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.

Upvotes: 0

Related Questions