0

I have an array of objects, where each object has a property (parentsList) indicating the category the current item belongs to, something like:

const data = [
    {
        ...other properties,
        "parentsList": [
            "Assets",
            "Icons"
        ],
    },
    {
        ...other properties,
        "parentsList": [
            "Assets",
            "Fonts"
        ],
    },
   {
      ...other properties,
       "parentsList": [
            "Programming",
            "JavaScript",
            "Docs"
        ],
   },
   {
      ...other properties,
       "parentsList": [
            "Programming",
            "JavaScript",
            "React",
            "Libraries",
        ],
   },
]

That means the first object belongs to assets/icons, the second to assets/fonts, third to programming/javascript/docs and so on.

I'm trying to map it to a tree-like view, where siblings should be under the same parent, something like:

const data = [
    {
        name: 'Assets',
        id: 'assets',
        children: [
            {
                name: 'Icons',
                id: 'assets/icons',
            },
            {
                name: 'Illustrations',
                id: 'assets/illustrations',
            },
        ],
    },
    {
        name: 'Programming',
        id: 'programming',
        children: [
            {
                name: 'JavaScript',
                id: 'programming/javascript',
                children: [
                    {
                        name: 'Docs',
                        id: 'programming/javascript/docs',
                    },
                    {
                        name: 'React',
                        id: 'programming/javascript/react',
                        children: [
                            {
                                name: 'Libraries',
                                id: 'programming/javascript/react/libraries',
                            },
                        ],
                    },
                ],
            },
        ],
    },
]

I imagine it's gonna be easier to traverse from the right, maybe with reduceRight(), but I can't seem to get it right.

Anyone would know how to achieve that?

Thanks!

2
  • 1
    Why not use an actual tree data structure instead? Commented Nov 19, 2020 at 18:02
  • 2
    what happens to "other properties"? Commented Nov 19, 2020 at 18:07

3 Answers 3

1

I tend to avoid using reduce because I find it difficult to read the code that has reduce in it. So, here is a non-reduce way.

const data = [
    {
        parentsList: [
            "Assets",
            "Icons"
        ],
    },
    {
        parentsList: [
            "Assets",
            "Fonts"
        ],
    },
    {
        parentsList: [
            "Programming",
            "JavaScript",
            "Docs"
        ],
    },
    {
        parentsList: [
            "Programming",
            "JavaScript",
            "React",
            "Libraries",
        ],
    },
];

const processedData = [];

for (const item of data) {
    const parents = [...item.parentsList].reverse();
    let children = processedData;
    const ids = [];
    while (parents.length > 0) {
        const parent = parents.pop();
        ids.push(parent.toLowerCase());
        let foundParent = false;
        for (const child of children) {
            if (child.name === parent) {
                children = child.children;
                foundParent = true;
                break;
            }
        }
        if (!foundParent) {
            const newChild = {name: parent, id: ids.join("/"), children: [],};
            children.push(newChild);
            children = newChild.children;
        }
    }
}

console.log(processedData);

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

Comments

1

You can do this as a combination of forEach and reduce and create a nested hierarchy based on the parentsList array.

const data = [{"parentsList":["Assets","Icons"]},{"parentsList":["Assets","Fonts"]},{"parentsList":["Programming","JavaScript","Docs"]},{"parentsList":["Programming","JavaScript","React","Libraries"]}]

const result = []

data.forEach(function({ parentsList, ...rest }) {
  let id = '';

  parentsList.reduce((r, name, i) => {
    id += (id.length ? '/' : '') + name.toLowerCase();

    if (!r[name]) {
      const value = { id, name }
      r[name] = {result: []}

      if (i != parentsList.length - 1) {
        value.children = r[name].result
      } else {
        Object.assign(value, rest)
      }

      r.result.push(value)
    }

    return r[name]
  }, this)

}, {result})

console.log(result)

Comments

1

A short approach by using nested objects as hash tables.

const
    data = [{ parentsList: ["Assets", "Icons"] }, { parentsList: ["Assets", "Fonts"] }, { parentsList: ["Programming", "JavaScript", "Docs"] }, { parentsList: ["Programming", "JavaScript", "React", "Libraries"] }],
    tree = data.reduce((t, { parentsList }) => {
        parentsList.reduce((r, name, i, a) => {
            const id = a.slice(0, i + 1).join('/').toLowerCase();
            if (!r[name]) {
                r[name] = { _: { name, id } };
                (r._.children ??= []).push(r[name]._);
            }
            return r[name];
        }, t);
        return t;
    }, { _: {} })._.children;

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

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.