javascriptnested-setsnested-set-model

Generating a Nested Set Model from a POJO


I have been playing around with some Nested Set Models (NSM). One thing I wanted to do is to be able to generate a NSM from a given JavaScript object.

For example, given the following object:

var data = {
  Clothes: {
    Jackets: {
      Waterproof: true,
      Insulated: true
    },
    Hats: true,
    Socks: true
  },
}

I'd like to generate an array of objects like so.

[
  {
    "name": "Clothes",
    "lft": 1,
    "rgt": 12
  },
  {
    "name": "Jackets",
    "lft": 2,
    "rgt": 7
  },
  {
    "name": "Waterproof",
    "lft": 3,
    "rgt": 4
  },
  {
    "name": "Insulated",
    "lft": 5,
    "rgt": 6
  },
  {
    "name": "Hats",
    "lft": 8,
    "rgt": 9
  },
  {
    "name": "Socks",
    "lft": 10,
    "rgt": 11
  }
]

That is - a depth first walk through the object, assigning an ID and counting the left and right edge for each object in the hierarchy. So that each node has a unique ID and the correct lft and rgt values for a NSM.

I've tried various approaches but just can't seem to get the result I am after...I had some success by altering the model to use properties for the node name and child nodes - i.e.

var data2 = {
  name: "Clothes",
  children: [{
      name: "Jackets",
      children: [{
        name: "Waterproof",
      }, {
        name: "Insulated"
      }]
    }, {
      name: "Hats"
    },
    {
      name: "Socks"
    }
  ]
};

function nestedSet(o, c, l = 0) {
  let n = {
    name: o.name,
    lft: l + 1
  };
  c.push(n);
  let r = n.lft;
  for (var x in o.children) {
    r = nestedSet(o.children[x], c, r);
  }

  n.rgt = r + 1;

  return n.rgt;
}

let out = [];
nestedSet(data2, out);
console.log(out)

This gives the correct result but requires altering the input data...is there a way to generate the same Nested Set Model using the original data object?


Solution

  • I actually managed to solve this in the end...I just forgot about it for a long while! Basically all that is required is to reclusively pass the Object.entries as kindly suggested in @CherryDT's comment. This way one can resolve the name/children to build the nested set model as required.

    var data = {
        Clothes: {
            Jackets: {
                Waterproof: {},
                Insulated: {},
            },
            Hats: {},
            Socks: {},
        },
    };
    
    function ns(node, stack = [], lft = 0) {
        var rgt = ++lft;
        var item = {
            name: node[0],
            lft: lft,
        };
        stack.push(item);
        Object.entries(node[1]).forEach(function (c) {
            rgt = ns(c, stack, rgt);
        });
        item.rgt = ++rgt;
        return rgt;
    }
    
    var result = [];
    ns(Object.entries(data)[0], result);
    console.log(result);