Home > Software design >  Conditionally expanding an Object recursively (nested children)
Conditionally expanding an Object recursively (nested children)

Time:05-09

Just so recently, I wrote my first question on merging arrays and objects and got such nice help. Facing another challenge in the arrays and objects world, that I never dealt with before I'll be audacious enough to ask again and hope to get a similar result of such nice answers. Hoping it could be also helpful to other viewers.

The goal of this is to have comments with recursive child comments.

As example data for this, there are posts and comments that got fetched.

let posts = [
    { id: '001a', topic: 'post topic', content: 'post content' }
]
let comments = [
    { id: '002a', post: '001a', parent: '001a', content: 'comment on post' },
    { id: '003a', post: '001a', parent: '002a', content: 'comment on comment' },
    { id: '004a', post: '001a', parent: '003a', content: 'comment on comments comment' },
]

If I should formulate it into words, I would say: conditionally on a comment-objects parent-key, this object should become a child-comment-object of its parent-comment-object. To achieve this, I came to think that it needs a functionality to create another array. Like:

let postComments = [
    {
        id: '002a',
        post: '001a',
        parent: '001a',
        content: 'comment on post',
        children: [
            { 
                id: '003a', 
                post: '001a', 
                parent: '002a', 
                content: 'comment on comment',
                children: [ {id: '004a', post: '001a', parent: '003a', content: 'comment on comments comment' }} ]
            }
        ]
    },
]

The approach I tried until now, was solving this without creating such a new array that extends itself. More like outputting the comments conditionally on it's parent in an each-block. Unfortunately with this approach I couldn't find a scalable way / to do it recursively -I'm not sure about the right terminology here. That's how I got to the approach this question is now about. As I'm still not sure if it's a good one in the case of having such data as above, any suggestion on a proven approach for such a problem is very welcome.

Seeing and understanding an Implementation of a scenario like the one above will probably give enough clarity to use it in more complex scenarios. I feel like this question is already on the harder side and don't want to overcomplicate it. In case it makes a difference for structuring an answer, the actual code of the learning project I'm working on and intending to implement it will look more like the snippet below.

let posts = [
    {
        publicKey: { pubKeyObj1: {} },
        post: { user: { userPubKeyObj1: {} }, topic: 'post topic', content: 'post content', timestamp: { ts: {} } }
    }
]

let comments = [
    {
        publicKey: { pubKeyObj2: {} },
        comment: { user: { userPubKeyObj2: {} }, post: { pubKeyObj1: {} }, parent: { pubKeyObj1: {} }, content: 'comment on post', timestamp: { ts: {} } }
    },
    {
        publicKey: { pubKeyObj3: {} },
        comment: { user: { userPubKeyObj1: {} }, post: { pubKeyObj1: {} }, parent: { pubKeyObj2: {} }, content: 'comment on comment', timestamp: { ts: {} } }
    }
]

CodePudding user response:

let posts = [{ id: '001a', topic: 'post topic', content: 'post content' }]
let comments = [
  { id: '002a', post: '001a', parent: '001a', content: 'comment on post' },
  { id: '003a', post: '001a', parent: '002a', content: 'comment on comment' },
  { id: '004a', post: '001a', parent: '003a', content: 'comment on comments comment' },
]

function getNestedComments(parentId) {
  const subComments = comments.filter((c) => c.parent === parentId)
  if (subComments.length === 0) return subComments
  subComments.forEach(c => {
    c.children = getNestedComments(c.id)
  })
  return subComments
}

let postComments = getNestedComments('001a')
console.log(postComments)

TypeScript Playground

CodePudding user response:

Here is another solution with no recursive function. It uses map-lookup.

let posts = [
    { id: '001a', topic: 'post topic', content: 'post content' }
]
let comments = [
    { id: '002a', post: '001a', parent: '001a', content: 'comment on post' },
    { id: '003a', post: '001a', parent: '002a', content: 'comment on comment' },
    { id: '004a', post: '001a', parent: '003a', content: 'comment on comments comment' }
]

let postComments = toNestedComments(comments, posts[0].id);
console.log(postComments);

function toNestedComments(comments, postId) {
    const nestedComments = [];
    const map = {};

    for (let i = 0; i < comments.length; i  ) {
        map[comments[i].id] = i;
    }

    for (let comment of comments) {
        if (comment.parent === postId) {
            nestedComments.push(comment);
        } else {
            if (!comments[map[comment.parent]].hasOwnProperty('children')) {
                comments[map[comment.parent]].children = [];
            }
            comments[map[comment.parent]].children.push(comment);
        }
    }

    return nestedComments;
}

  • Related