Nest same object n times in array

Here is an example object array I’d like to transform into a much more complex structure:

[
  {
    "name": "Blue (3/8)"
  }, 
  {
    "name": "Green (5/8)"
  }
]

To give some context, I am trying to create a visual tree diagram demonstrating the probabilities of different-colored marbles being drawn out of a bag (that’s why you see a number followed by a fraction). Below is the statistical-type tree I’m referring to.

This is the statistical tree I'm talking about

I need to repeat adding this same array into an object in a nested format, assigning it to a key titled “children”. This needs to be done n amount of times (based on the number of marble draws). The below object would be a result of nesting the same object array 3 times and then it would make a nice tree (using d3.js):

var treeData =
{
  "children": [   // Level 1
   { 
      "name": "Blue (3/8)",
      "children": [   // Level 2
         { "name": "Blue (3/8)",
           "children": [   // Level 3
              { "name": "Blue (3/8)" },
              { "name": "Green (5/8)" }
           ]
         },
         { "name": "Green (5/8)",
           "children": [   // Level 3
              { "name": "Blue (3/8)" },
              { "name": "Green (5/8)" }
           ]
         }
      ]
   },
   { 
      "name": "Green (5/8)",
      "children": [   // Level 2
         { "name": "Blue (3/8)",
           "children": [   // Level 3
              { "name": "Blue (3/8)" },
              { "name": "Green (5/8)" }
           ]
         },
         { "name": "Green (5/8)",
           "children": [   // Level 3
              { "name": "Blue (3/8)" },
              { "name": "Green (5/8)" }
           ]
         }
      ]
    }
  ]
};
      
// set the dimensions and margins of the diagram
var margin = {top: 20, right: 90, bottom: 30, left: 90},
    width = 660 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// declares a tree layout and assigns the size
var treemap = d3.tree()
    .size([height, width]);

//  assigns the data to a hierarchy using parent-child relationships
var nodes = d3.hierarchy(treeData, function(d) {
    return d.children;
  });

// maps the node data to the tree layout
nodes = treemap(nodes);

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom),
    g = svg.append("g")
      .attr("transform",
            "translate(" + margin.left + "," + margin.top + ")");

// adds the links between the nodes
var link = g.selectAll(".link")
    .data( nodes.descendants().slice(1))
  .enter().append("path")
    .attr("class", "link")
    .attr("d", function(d) {
       return "M" + d.y + "," + d.x
         + "C" + (d.y + d.parent.y) / 2 + "," + d.x
         + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x
         + " " + d.parent.y + "," + d.parent.x;
       });

// adds each node as a group
var node = g.selectAll(".node")
    .data(nodes.descendants())
  .enter().append("g")
    .attr("class", function(d) { 
      return "node" + 
        (d.children ? " node--internal" : " node--leaf"); })
    .attr("transform", function(d) { 
      return "translate(" + d.y + "," + d.x + ")"; });

// adds the circle to the node
node.append("circle")
  .attr("r", 10);

// adds the text to the node
node.append("text")
  .attr("dy", ".35em")
  .attr("x", function(d) { return d.children ? -13 : 13; })
  .style("text-anchor", function(d) { 
    return d.children ? "end" : "start"; })
  .text(function(d) { return d.data.name; });
.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.node text { font: 12px sans-serif; }

.node--internal text {
  text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>

This happens to be the format of the object needed for d3.js to display the desired output in a tree. Here is the closest thing I could salvage to get my desired output:

var objectArray = [{
  name: "Blue (5/8)"
}, {
  name: "Green (3/8)"
}, {
  name: "Blue (5/8)"
}, {
  name: "Green (3/8)"
}, {
  name: "Blue (5/8)"
}, {
  name: "Green (3/8)"
}];

var newArr = objectArray.reverse().reduce((acc, item) => {
    return [{
        ...item,
        children: acc
    }]
});

console.log(newArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Answer

You could take a simple mapping with a look for the wanted depth.

const
    create = (data, depth) => data.map((o, i) => ({
        name: `${o.name} (${o.count}/${o.total})`,
        ...(depth > 1 ? { children: create(data.map(({ name, total, count }, j) => ({ name, count: count - (i === j), total: total - 1 })), depth - 1) } : {})
    })),
    data = [{ name: "Blue", count: 3, total: 8 }, { name: "Green", count: 5, total: 8 }],
    result = create(data, 3);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Leave a Reply

Your email address will not be published. Required fields are marked *