T00rk
T00rk

Reputation: 2287

Reduce javascript array with nested children

I'm trying to create a help page for a website using mongodb and graphql. I've made this schema (joiGoose Schema) for the help pages:

constructor()
{
    const joiSchema = joi.object
    ({
        url: joi.string().required(),
        title: joi.string().required(),

        content: joi.string().required(),

        children: joi.array().items
        (
            joi.string().meta({ _mongoose: { type: "ObjectId", ref: "Help" } }).optional(),
        ).meta({ _mongoose : { _id: false, timestamps: false } }),

        published: joi.boolean().default(true).required(),

        createdAt: joi.date().required().default(() => moment()),
        updatedAt: joi.date().required().default(() => moment()),
    });
        
    const mongooseSchema = super(joigoose.convert(joiSchema));
    return mongooseSchema;
}

The first thing I want to do is to give the summary of the help section. I get all the pages from mongodb using the following query :

const allHelpPages = await MONGO.Help.find();`

It gives me an array containing all the help pages. As each page can have multiple nested children pages (there is no maximum level), what I need is to reduce the array with the children for all the pages.

Example:

const allPages = 
[
    {
        id: 1,
        title: Test 1,
        children:
        [
            2,
            3
        ]
    },
    {
        id: 2,
        title: Test 2,
        children:
        [
            4,
            5
        ]
    }
    {
        id: 3,
        title: Test 3,
        children:
        [
            6
        ]
    }
    {
        id: 4,
        title: Test 4
    }
    {
        id: 5,
        title: Test 5
    }
    {
        id: 6,
        title: Test 6,
        children:
        [
            7
        ]
    }
    {
        id: 7,
        title: Test 7
    }
]

Should end to :

[
    {
        id: 1,
        title: Test 1,
        children:
        [
            {
                id: 2,
                title: Test 2,
                children:
                [
                    {
                        id: 4,
                        title: Test 4
                    },
                    {
                        id: 5,
                        title: Test 5
                    }
                ]
            },
            {
                id: 3,
                title: Test 3,
                children:
                [
                    {
                        id: 6,
                        title: Test 6,
                        children:
                        [
                            {
                                id: 7,
                                title: Test 7
                            }
                        ]
                    }
                ]
            }
        ]
    },
]

Upvotes: 0

Views: 463

Answers (1)

Nenad Vracar
Nenad Vracar

Reputation: 122077

You could create one object that you can use as a lookup table where keys are ids of objects. And then based on that object you can modify children arrays with actual objects and in the end just filter out objects that are children of some other object.

const data = [{"id":1,"title":"Test 1","children":[2,3]},{"id":2,"title":"Test 2","children":[4,5]},{"id":3,"title":"Test 3","children":[6]},{"id":4,"title":"Test 4"},{"id":5,"title":"Test 5"},{"id":6,"title":"Test 6","children":[7]},{"id":7,"title":"Test 7"}]

const map = {}, isChild = {}

for (let o of data) {
  map[o.id] = o
}

const result = Object.entries(map)
  .map(([k, v]) => {
    const children = v.children;

    if (children) children.forEach((id, i) => {
      isChild[id] = true
      children[i] = map[id]
    })

    return { ...v, children}
  })
  .filter(({ id }) => !isChild[id])

console.log(result)

Upvotes: 1

Related Questions