1

I need to convert an array of paths:

const paths = [
  "test1",
  "test1.test1children"
]

to an object with the following structure:

{
  name: "test1",
  id: "test1",
  children: [...test1 children],
}

I try with a reducer and a recursive function per array item, it is works but is pretty slow. Any suggestions to improve this code are welcome. Thanks in advance.

  const iterRow = (row, obj) => {
    if (Array.isArray(row) && row.length !== 0) {
      const [name, ...pathStack] = row;
      const found = (n) => obj.children.find((i) => i.name === n);

      if (obj.name === name) {
        iterRow(pathStack, obj);
      } else {
        const newLeaf = {
          name,
          id: `${obj.id}.${name}`,
          children: [],
        };
        if (!found(name)) {
          obj.children.push(newLeaf);
        }
        iterRow(pathStack, found(name));
      }
    }
  };
  
const argOptionsProcessed = (rows) => rows.reduce((acc, row) => {
    const path = row.split('.');
    const found = () => acc.find((item) => path[0] === item.name);
    if (!found()) {
      acc.push({ name: path[0], id: path[0], children: [] });
    }
    iterRow(path, found());
    return acc;
  }, []);
  
  
  
const paths = [
"test1",
"test1.test1children",
"test1.test1children.test1subchildren",
"test1.test1children.test1subchildren.test1subsubchildren",
"tets2",
"test2.test2children",
]


console.log(argOptionsProcessed(paths));

3
  • 1
    This question may be better suited for codereview.stackexchange.com Commented Jun 25, 2020 at 8:05
  • Btw i am not sure if your code is working. I get back 3 instead of 2 objects. The object with id test2 is contained twice. Commented Jun 25, 2020 at 8:08
  • 1
    And better provide an abstract explanation of the algorithm you used, so people can help you improving this algorithm and have not to understand your whole code. Commented Jun 25, 2020 at 8:10

3 Answers 3

3

You could do this without recursion using forEach and reduce methods and one object to keep nested levels.

const paths = [
  "test1",
  "test1.test1children",
  "test1.test1children.test1subchildren",
  "test1.test1children.test1subchildren.test1subsubchildren",
  "test2",
  "test2.test2children",
]

const result = []
const levels = { result }

paths.forEach(path => {
  let id = '';

  path.split('.').reduce((r, name, i, a) => {
    id += (id ? '.' : '') + name;

    if (!r[name]) {
      r[name] = { result: [] }
      r.result.push({ name, id, children: r[name].result })
    }

    return r[name]
  }, levels)
})

console.log(result)

Sign up to request clarification or add additional context in comments.

1 Comment

You are right! thank you very much! ill try in my own code thanks again
0

You could take a shorter approach by reducing the array and the path and search for same name.

const
    getTree = data => data.reduce((tree, path) => {
        path.split('.').reduce((t, name, i, a) => {
            let temp = t.find(q => q.name === name);
            if (!temp) t.push(temp = { name, id: a.slice(0, i + 1).join('.'), children: [] });
            return temp.children;
        }, tree);
        return tree;
    }, []),
    paths = ["test1", "test1.test1children", "test1.test1children.test1subchildren", "test1.test1children.test1subchildren.test1subsubchildren", "tets2", "test2.test2children"]

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

Comments

0

Here's another solution.

const paths = [
  "test1",
  "test1.test1children",
  "test1.test1children.test1subchildren",
  "test1.test1children.test1subchildren.test1subsubchildren",
  "test2",
  "test2.test2children",
];

console.log(untangle(paths));

function untangle(paths) {
  return paths.reduce( (a, v) => {
    const path = v.split(".");
    if (path.length < 2) {
      const l0 = path[0];
      let level0 = {name: l0, id: l0, children: []};
      let tryChildren = paths
        .filter( p => ~p.indexOf(".") && p.startsWith(l0) )
      if (tryChildren.length) {
        tryChildren = tryChildren.map( p => p.substr( p.indexOf(".")+1 ) );
        level0.children = level0.children.concat(untangle(tryChildren));
      }
      a.push(level0);
    }
    return a;
  }, []);
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

Comments

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.