0

I'm trying to write a function called customAdd() that forms the following tree:

    let obj = []
    
   let obj1 =  {
    key: "detail1Tests",
    id : "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
  }

  let obj2 = {key:"detail1Tests.detail2Tests",id:"5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"}

  let obj3 = {key:"detail1Tests.detail2Tests.detail3Tests",id:"0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"}


  let result = this.customAdd(obj, obj1);
  console.log(result);

  let result1 = this.customAdd(result, obj2);
  console.log(result1);

  let result2 = this.customAdd(result1, obj3);
  console.log(result2);
  };

result should hold the value of :

children: {
    detail1Tests: [{
            id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
            ]

result1 should be equal to :

children: {
    detail1Tests: [{
            id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
            children: {
                detail1Tests.detail2Tests: [{
                        id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"
                    }
                ]
                ]

and result2 should be:

children: {
    detail1Tests: [{
            id: " 94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e "
            children: {
                detail1Tests.detail2Tests: [{
                        id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"
                        children: {
                            detail1Tests.detail2Tests.detail3Tests: [{
                                    id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
                                }
                            ]
                        }
                    }
                ]
                ]

and so on...

this is the function i built which is only working on the first level:

  customAdd(obj , subObj){
    let obj2 = {children: {[subObj.key]: [{id: subObj.id}] }}
    if(obj.children){
      let obj3 = obj.children;
     var kyz = Object.keys(obj3);
     let obj4 = obj3[kyz[0]]
      this.customAdd(obj4 , subObj)
    }
    else {
      return {...obj,...obj2};
    }
   
  }

any ideas on how to achieve this is appreciated.

Wissam
  • 35
  • 7
  • 1
    That's an inefficient data structure to go for. Why arrays? Why not objects keyed by `id`? That will have an impact on searching data in that structure... – trincot Sep 28 '20 at 12:20

3 Answers3

0

Sounds a lot like you want to create something like lodash 'set' or ramda 'assocPath'

https://lodash.com/docs/4.17.15#set

https://ramdajs.com/docs/#assocPath

designreact
  • 141
  • 1
  • 10
  • Looks like the lodash set method is a proxy for https://github.com/lodash/lodash/blob/master/.internal/baseSet.js – designreact Sep 28 '20 at 08:49
0

The Function customAdd() had some bugs with what it's trying to achieve. it needs some modifications and it becomes like so:

customAdd(obj , subObj){
    
    if(obj.children){
      let obj3 = obj.children;
     var kyz = Object.keys(obj3);
     let obj4 = obj3[kyz[0]]
    return  this.customAdd(obj4[0] , subObj)
    }
    else {
      obj.children=obj2.children;
      return ;
    }
   
  }

keeping in mind that the object passed to this function would be modified , so if we need to hold on to result, result1, result2 independently , we need to 'deep copy' the object after passing it to our function and this could be achieved through the syntax:

let result = JSON.parse(JSON.stringify(obj));

so the code becomes :

this.customAdd(obj, obj1);
  var result = JSON.parse(JSON.stringify(obj));
  console.log(result);  

  this.customAdd(obj, obj2);
  var result1 = JSON.parse(JSON.stringify(obj));;
  console.log(result1);

  this.customAdd(obj,obj3);
  var result2 = JSON.parse(JSON.stringify(obj));;
  console.log(result2);
Wissam
  • 35
  • 7
0

As designreact suggested, this could easily be built atop Ramda's assocPath (or probably atop the lodash equivalent.)

const customAdd = ({key, ...rest}, o) =>
  assocPath (key .split ('.') .flatMap (k => ['children', k, 0]), rest, o)


let obj1 =  {
  key: "detail1Tests",
  id : "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
}

let obj2 = {
  key: "detail1Tests.detail2Tests",
  id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838"}

let obj3 = {
  key: "detail1Tests.detail2Tests.detail3Tests",
  id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
}


const results1 = customAdd (obj1, {})
const results2 = customAdd (obj2, results1)
const results3 = customAdd (obj3, results2)

console .log (results1)
console .log (results2)
console .log (results3)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js"></script>
<script> const {assocPath} = R </script>

We split the key into parts, and then each part gets a preceding "children" and a succeeding 0, so that, for instance, "detail1Tests.detail2Tests" becomes ["children", "detail1Tests", 0, "children", "detail2Tests", 0], which is the format used by Ramda's assocPath.

But there's no need to pull in Ramda just for this. It's easy enough to write our own version of assocPath, which I've done several times for other questions on StackOverflow, including in this recent answer. It is built atop another Ramda-inspired function, assoc, and together they look like this:

const assoc = (p, v, o) => 
  Number .isInteger (p) && Array .isArray (o)
    ? [... o .slice (0, p), v, ... o .slice (p + 1)]
    : {... o, [p]: v}

const assocPath  = ([p, ... ps], v, o) => 
  p == undefined
    ? o
    : ps.length == 0
      ? assoc (p, v, o)
      : assoc (p, assocPath (ps, v, o [p] || (o [p] = Number.isInteger (ps [0]) ? [] : {})), o)

However, I would suggest that you rethink your output structure. Unless you need that format for consistency with an external system, it's pretty terrible. This would seem better to me:

{
    detail1Tests: {
        id: "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e",
        detail2Tests: {
            id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838",
            detail3Tests: {
                id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
            }
        }
    }
}

and could be easily achieved with

const customAdd = ({key, ...rest}, o) =>
  assocPath (key .split ('.'), rest, o)

Or, if you really want the children node:

{
    children: {
        detail1Tests: {
            id: "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e"
            children: {
                detail2Tests: {
                    id: "5b091b37a9efc9d0567a4beac0bb20fcdf9796f4b71e239da6ac0c53e3488838",
                    children: {
                        detail3Tests: {
                            id: "0b60c29d6e309be95ef33b0ad137623c5712a9a47613ce5e561871001c71bd3b"
                        }
                    }
                }
            }
        }
    }
}

you could use

const customAdd = ({key, ...rest}, o) =>
  assocPath (key .split ('.') .flatMap (k => ['children', k]), rest, o)

The extra array wrapper seems to add nothing useful.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103