0

I have a Javascript array of the form:

var array = [Company_stock_content\\LightRhythmVisuals\\LRV_HD", "Big Media Test\\ArtificiallyAwake\\AA_HD", "Big Media Test\\Company\\TestCards_3840x2160\\TestCards_1920x1080",...]

I need to construct a JSON object of the form:

[
   {
        "data" : "parent",
        "children" : [
            {
                "data" : "child1",            
                "children" : [ ]
            }
        ]
    }
]

so for each top level node in the array it can have multiple children that all have children. for example if we take the array snipper provided, the corresponding JSON object would look as such

[{
    "data": "Company_stock_content",
    "children": [{
        "data": "LightRhythmVisuals",
        "children": [{
            "data": "LRV_HD"
        }]
    }]
}, {
    "data": "Big Media Test",
    "children": [{
        "data": "ArtificiallyAwake",
        "children": [{
            "data": "AA_HD"
        }]
    }, {
        "data": "Company",
        "children": [{
            "data": "TestCards_3840x2160"
        }]
    }]
}]

How can I this structure from the given data bearing in mind the original array can have tens of thousands of entries?

5
  • How does the array relate to the data format of the JSON? Commented Dec 6, 2021 at 14:17
  • Then, may you update the example to show the actual output you'd like? Commented Dec 6, 2021 at 14:21
  • 1
    You could go down the recursive route, but I would recommend you start of by looping through the main array, splitting up the strings, and then looping over that. Commented Dec 6, 2021 at 14:33
  • thank you for your patience and reply, can you explain a bit more about what you mean by splitting the strings? into a new array? Commented Dec 6, 2021 at 14:34
  • It's not clear what the specific issue is--this appears to be a matter of keeping a collection of nodes and recursively processing the strings (although I'd start the nodes at a "root" node and for the output use the root node's children). Commented Dec 6, 2021 at 14:47

2 Answers 2

1

Here's a way to convert lineages into a hierarchy. A simple guard against cycles is to keep a parent pointer and take a lazy approach when setting parent-child relations (if conflicting relations are described, the last one wins).

Consider the lineages:

["A\\E", "A\\F", "B\\G\\I", "B\\G\\J", "C\\H"]

Describing this tree:

  A     B     C
  |     |     |
E   F   G     H 
        |
       I J

const array = ["A\\E", "A\\F", "B\\G\\I", "B\\G\\J", "C\\H"];
const lineages = array.map(string => string.split("\\"));

let nodes = {}

function createParentChild(parentName, childName) {
  const nodeNamed = name => {
    if (nodes[name]) return nodes[name];
    nodes[name] = { name, children: [], closed: false };
    return nodes[name];
  };
  let parent = nodeNamed(parentName)
  let child = nodeNamed(childName)
  if (child.parent) {
    // if this child already has a parent, we have an ill-formed input
    // fix by undoing the existing parent relation, remove the child from its current parent
    child.parent.children = child.parent.children.filter(c => c.name !== childName);
  }
  child.parent = parent
  if (!parent.children.includes(child))
    parent.children.push(child);
}

lineages.forEach(lineage => {
   for (i=0; i<lineage.length-1; i++) {
     createParentChild(lineage[i], lineage[i+1]);
   }
 })

 let roots = Object.values(nodes).filter(node => !node.parent)
 Object.values(nodes).forEach(node => delete node.parent)
 
 console.log(roots)

This approach also works for the (maybe pathological) input like this

// Here, "F" appears as a descendant of "A" and "B"
["A\\E", "A\\F", "B\\G\\I", "B\\G\\F", "C\\H"]

Last one wins:

  A     B     C
  |     |     |
  E     G     H 
        |
       I F
Sign up to request clarification or add additional context in comments.

3 Comments

if i want to add an extra parameter to the json object like state in which i have a boolean closed = true inside your nodeNamed const how would you add that functionality?
@Carpathea, sure. Either add the other props in the nodeNamed function, as I did in the edit, or do so to the parent and child objects after they are returned.
amazing thank you!
1

Something like this:

const array = ["Company_stock_content\\LightRhythmVisuals\\LRV_HD", "Big Media Test\\ArtificiallyAwake\\AA_HD", "Big Media Test\\Company\\TestCards_3840x2160\\TestCards_1920x1080"];

const result = array.reduce((acc, item) => {
  item.split('\\').forEach((entry, index, splits) => {
    acc[entry] = acc[entry] || { data: entry, children: [] };
    if (index > 0) {
      if (!acc[splits[index-1]].children.some(child => child.data === entry)) {
        acc[splits[index-1]].children.push(acc[entry]);
      }
    } else {
      if (!acc.children.some(root => root.data === entry)) {
        acc.children.push(acc[entry]);
      }
    }
  });
  return acc;
}, { children: []}).children;

console.log(result);

6 Comments

Then to turn it into JSON JSON.stringify(result)
this method has behaviour i can only explain in a picture i.imgur.com/yxbfveN.png each entry should be unique like a parent child hierarchy but each time a node could exist its being placed within the result
@Carpathea a small change to remove duplicates in children. updated.
@ProGu - I think the OP means to describe lineages in the input, and aims to convert them into a hierarchy. This answer will produce a cyclic graph if nodes at different hierarchical levels are matched. e.g. Given["A\\B\\C\\D", "E\\F\\B\\G"] , B is not the child of both A and F. (At least that's how I read the question)
@danh in the sample array, elements are not isolated. Big Media Test is combined, so I assume C & G are both children of B. And you are correct, if A appears somewhere as child of B for example, it's cyclic
|

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.