BARNOWL
BARNOWL

Reputation: 3589

How to use reduce to retrieve values from deep nested array objects

I'm trying to get a list of all users with the author.username property. So that means access posts author, comments author, and commentReplies author. I want an returned array with a list of usernames.

expected output

["blankman", "blankman2", "barnowl", "barnowl2", "blankman3"]

the wrong output

["blankman", "blankman2blankman3", "barnowl2"]

This is how im doing it but i think im doing it wrong. as its not dynamic

const arr = [{
  "id": 5,
  "title": "buttercup",
  "postContent": "dsdsfsfsfsfsfs",
  "likedByMe": false,
  "likeCounts": 0,
  "userId": 1,
  "createdAt": "2020-08-17T03:41:16.749Z",
  "updatedAt": "2020-08-17T03:41:16.749Z",
  "author": {
    "username": "blankman",
    "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
    "bio": null
  },
  "Likes": [],
  "Comments": [{
      "id": 46,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [{
        "id": 18,
        "replyBody": "fsfsffsffsffsf",
        "userId": 2,
        "commentId": 46,
        "postId": 5,
        "createdAt": "2020-08-21T16:40:47.205Z",
        "updatedAt": "2020-08-21T16:40:47.205Z",
        "author": {
          "username": "barnowl",
          "gravatar": null,
          "bio": null
        }
      }],
      "author": {
        "username": "blankman2",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
    {
      "id": 47,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [{
        "id": 18,
        "replyBody": "fsfsffsffsffsf",
        "userId": 2,
        "commentId": 46,
        "postId": 5,
        "createdAt": "2020-08-21T16:40:47.205Z",
        "updatedAt": "2020-08-21T16:40:47.205Z",
        "author": {
          "username": "barnowl2",
          "gravatar": null,
          "bio": null
        }
      }],
      "author": {
        "username": "blankman3",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },

  ],
  "RePosts": [],
  "RepostedByMe": false
}]

const findUsers = arr.reduce((acc, cv) => {
  const users = [
    ...acc,
    cv.author.username, // get first level author.username
    cv.Comments.reduce((acc, cv) => acc.concat(cv.author.username), ""), // get comments author.username 
    cv.Comments.reduce((acc, cv) => cv.commentReplies.reduce((acc, cv) => acc.concat(cv.author.username), ""), "") // get comment replies author.username

  ]
  return users
}, [])
console.log(findUsers)

Upvotes: 1

Views: 786

Answers (6)

Nina Scholz
Nina Scholz

Reputation: 386604

You could take a recursion by handing over the key of the parent object.

It works with three steps,

  1. check if the handed over value is an array and if not return an empty array,
  2. check if parent key is author and if property username exist, then take the value of the username property.
  3. get the entries of the object and get all usernames with a recursion by handing over the parent key.

const
    getUsernames = (object, parent) => {
        if (!object || typeof object !== 'object') return [];
        const temp = [];
        if (parent === 'author' && 'username' in object) temp.push(object.username);
        return [
            ...temp,
            ...Object.entries(object).flatMap(([k, v]) => getUsernames(v, k))
        ];
    },
    data = [{ id: 5, title: "buttercup", postContent: "dsdsfsfsfsfsfs", likedByMe: false, likeCounts: 0, userId: 1, createdAt: "2020-08-17T03:41:16.749Z", updatedAt: "2020-08-17T03:41:16.749Z", author: { username: "blankman", gravatar: "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", bio: null }, Likes: [], Comments: [{ id: 46, comment_body: "fsfsfsfsfsf", gifUrl: "", userId: 1, postId: 5, createdAt: "2020-08-18T04:46:08.946Z", updatedAt: "2020-08-18T04:46:08.946Z", commentReplies: [{ id: 18, replyBody: "fsfsffsffsffsf", userId: 2, commentId: 46, postId: 5, createdAt: "2020-08-21T16:40:47.205Z", updatedAt: "2020-08-21T16:40:47.205Z", author: { username: "barnowl", gravatar: null, bio: null } }], author: { username: "blankman2", gravatar: "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", bio: null } }, { id: 47, comment_body: "fsfsfsfsfsf", gifUrl: "", userId: 1, postId: 5, createdAt: "2020-08-18T04:46:08.946Z", updatedAt: "2020-08-18T04:46:08.946Z", commentReplies: [{ id: 18, replyBody: "fsfsffsffsffsf", userId: 2, commentId: 46, postId: 5, createdAt: "2020-08-21T16:40:47.205Z", updatedAt: "2020-08-21T16:40:47.205Z", author: { username: "barnowl2", gravatar: null, bio: null } }], author: { username: "blankman3", gravatar: "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", bio: null } }], RePosts: [], RepostedByMe: false }],
    result = getUsernames(data);

console.log(result);

Upvotes: 1

trincot
trincot

Reputation: 350272

You would need a recursive function. With flatMap you can ensure that the recursively returned arrays are put together as a single flat array.

Here is a possible implementation that doesn't depend on property names like "Comments" and "commentReplies", but just scans all arrays:

const collect = arr => Array.isArray(arr) ? arr.flatMap(value => 
    [value.author?.username].concat(Object.values(value).flatMap(collect))
).filter(Boolean) : [];

// demo
const arr = [{"id": 5,"title": "buttercup","postContent": "dsdsfsfsfsfsfs","likedByMe": false,"likeCounts": 0,"userId": 1,"createdAt": "2020-08-17T03:41:16.749Z","updatedAt": "2020-08-17T03:41:16.749Z","author": {"username": "blankman","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null},"Likes": [],"Comments": [{"id": 46,"comment_body": "fsfsfsfsfsf","gifUrl": "","userId": 1,"postId": 5,"createdAt": "2020-08-18T04:46:08.946Z","updatedAt": "2020-08-18T04:46:08.946Z","commentReplies": [{"id": 18,"replyBody": "fsfsffsffsffsf","userId": 2,"commentId": 46,"postId": 5,"createdAt": "2020-08-21T16:40:47.205Z","updatedAt": "2020-08-21T16:40:47.205Z","author": {"username": "barnowl","gravatar": null,"bio": null}}],"author": {"username": "blankman2","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null}},{"id": 47,"comment_body": "fsfsfsfsfsf","gifUrl": "","userId": 1,"postId": 5,"createdAt": "2020-08-18T04:46:08.946Z","updatedAt": "2020-08-18T04:46:08.946Z","commentReplies": [{"id": 18,"replyBody": "fsfsffsffsffsf","userId": 2,"commentId": 46,"postId": 5,"createdAt": "2020-08-21T16:40:47.205Z","updatedAt": "2020-08-21T16:40:47.205Z","author": {"username": "barnowl2","gravatar": null,"bio": null}}],"author": {"username": "blankman3","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null}},],"RePosts": [],"RepostedByMe": false}];
console.log(collect(arr));

If you find this functional expression syntax confusing, then here is something similar in a more verbose manner:

function collect(arr) {
    if (!Array.isArray(arr)) return [];
    let result = [];
    for (let obj of arr) {
        result.push(obj.author?.username);
        for (let key in obj) {
            result = result.concat(collect(obj[key]));
        }
    }
    return result.filter(Boolean); // exclude null/undefined values
}
// demo
const arr = [{"id": 5,"title": "buttercup","postContent": "dsdsfsfsfsfsfs","likedByMe": false,"likeCounts": 0,"userId": 1,"createdAt": "2020-08-17T03:41:16.749Z","updatedAt": "2020-08-17T03:41:16.749Z","author": {"username": "blankman","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null},"Likes": [],"Comments": [{"id": 46,"comment_body": "fsfsfsfsfsf","gifUrl": "","userId": 1,"postId": 5,"createdAt": "2020-08-18T04:46:08.946Z","updatedAt": "2020-08-18T04:46:08.946Z","commentReplies": [{"id": 18,"replyBody": "fsfsffsffsffsf","userId": 2,"commentId": 46,"postId": 5,"createdAt": "2020-08-21T16:40:47.205Z","updatedAt": "2020-08-21T16:40:47.205Z","author": {"username": "barnowl","gravatar": null,"bio": null}}],"author": {"username": "blankman2","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null}},{"id": 47,"comment_body": "fsfsfsfsfsf","gifUrl": "","userId": 1,"postId": 5,"createdAt": "2020-08-18T04:46:08.946Z","updatedAt": "2020-08-18T04:46:08.946Z","commentReplies": [{"id": 18,"replyBody": "fsfsffsffsffsf","userId": 2,"commentId": 46,"postId": 5,"createdAt": "2020-08-21T16:40:47.205Z","updatedAt": "2020-08-21T16:40:47.205Z","author": {"username": "barnowl2","gravatar": null,"bio": null}}],"author": {"username": "blankman3","gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN","bio": null}},],"RePosts": [],"RepostedByMe": false}];
console.log(collect(arr));

Upvotes: 1

GirkovArpa
GirkovArpa

Reputation: 4912

This is the schema of your object:

  • author.username
  • Comments[i].author.username
  • Comments[i].commentReplies[i].author.username

You asked specifically for a way using .reduce so here is one solution using .reduce along with lots of destructuring:

const usernames = arr.reduce((names, { author: { username }, Comments }) => {
  names.push(username);
  Comments.forEach(({ author: { username }, commentReplies }) => {
    names.push(username);
    commentReplies.forEach(({ author: { username } }) => names.push(username));
  });
  return names;
}, []);

Live example:

'use strict';

const arr = [
  {
    "id": 5,
    "title": "buttercup",
    "postContent": "dsdsfsfsfsfsfs",
    "likedByMe": false,
    "likeCounts": 0,
    "userId": 1,
    "createdAt": "2020-08-17T03:41:16.749Z",
    "updatedAt": "2020-08-17T03:41:16.749Z",
    "author": {
      "username": "blankman",
      "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
      "bio": null
    },
    "Likes": [],
    "Comments": [{
      "id": 46,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [{
        "id": 18,
        "replyBody": "fsfsffsffsffsf",
        "userId": 2,
        "commentId": 46,
        "postId": 5,
        "createdAt": "2020-08-21T16:40:47.205Z",
        "updatedAt": "2020-08-21T16:40:47.205Z",
        "author": {
          "username": "barnowl",
          "gravatar": null,
          "bio": null
        }
      }],
      "author": {
        "username": "blankman2",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
    {
      "id": 47,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [{
        "id": 18,
        "replyBody": "fsfsffsffsffsf",
        "userId": 2,
        "commentId": 46,
        "postId": 5,
        "createdAt": "2020-08-21T16:40:47.205Z",
        "updatedAt": "2020-08-21T16:40:47.205Z",
        "author": {
          "username": "barnowl2",
          "gravatar": null,
          "bio": null
        }
      }],
      "author": {
        "username": "blankman3",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
    ],
    "RePosts": [],
    "RepostedByMe": false
  }
];

// author.username
// Comments[i].author.username
// Comments[i].commentReplies[i].author.username

const usernames = arr.reduce((names, { author: { username }, Comments }) => {
  names.push(username);
  Comments.forEach(({ author: { username }, commentReplies }) => {
    names.push(username);
    commentReplies.forEach(({ author: { username } }) => names.push(username));
  });
  return names;
}, []);

console.log(usernames);
// => [ 'blankman', 'blankman2', 'barnowl', 'blankman3', 'barnowl2' ]

Upvotes: 1

PotatoParser
PotatoParser

Reputation: 1058

You can do this iteratively or recursively:

const arr = [{
  "id": 5,
  "title": "buttercup",
  "postContent": "dsdsfsfsfsfsfs",
  "likedByMe": false,
  "likeCounts": 0,
  "userId": 1,
  "createdAt": "2020-08-17T03:41:16.749Z",
  "updatedAt": "2020-08-17T03:41:16.749Z",
  "author": {
    "username": "blankman",
    "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
    "bio": null
  },
  "Likes": [],
  "Comments": [
    {
      "id": 46,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [
        {
          "id": 18,
          "replyBody": "fsfsffsffsffsf",
          "userId": 2,
          "commentId": 46,
          "postId": 5,
          "createdAt": "2020-08-21T16:40:47.205Z",
          "updatedAt": "2020-08-21T16:40:47.205Z",
          "author": {
            "username": "barnowl",
            "gravatar": null,
            "bio": null
          }
        }
      ],
      "author": {
        "username": "blankman2",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
     {
      "id": 47,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [
        {
          "id": 18,
          "replyBody": "fsfsffsffsffsf",
          "userId": 2,
          "commentId": 46,
          "postId": 5,
          "createdAt": "2020-08-21T16:40:47.205Z",
          "updatedAt": "2020-08-21T16:40:47.205Z",
          "author": {
            "username": "barnowl2",
            "gravatar": null,
            "bio": null
          }
        }
      ],
      "author": {
        "username": "blankman3",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
    
  ],
  "RePosts": [],
  "RepostedByMe": false
}]

// No recursion
function getUsers(array) {
  return array.flatMap(v => {
    let comments = v.Comments;
    return [v.author.username].concat(comments.flatMap(c => {
      let replies = c.commentReplies;
      return [c.author.username].concat(replies.flatMap(r => {
        return r.author.username;
      }));
    }));
  });
}

// Recursion
function recursGetUsers(array) {
  if (!array) return [];
  return array.flatMap(v => {
    return [v.author.username]
    .concat(recursGetUsers(v.Comments))
    .concat(recursGetUsers(v.commentReplies));
  });
}

console.log(getUsers(arr));

console.log(recursGetUsers(arr));

Upvotes: 3

Sphinx
Sphinx

Reputation: 10729

If your schema is settled down, your approach is a solution and almost reaching the goal (though recursion is another solution). the problem in your codes is uses Array.concat then return String. You should return one [] same as the most outer reduce.

Check below snippet:

const arr = [{
  "id": 5,
  "title": "buttercup",
  "postContent": "dsdsfsfsfsfsfs",
  "likedByMe": false,
  "likeCounts": 0,
  "userId": 1,
  "createdAt": "2020-08-17T03:41:16.749Z",
  "updatedAt": "2020-08-17T03:41:16.749Z",
  "author": {
    "username": "blankman",
    "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
    "bio": null
  },
  "Likes": [],
  "Comments": [
    {
      "id": 46,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [
        {
          "id": 18,
          "replyBody": "fsfsffsffsffsf",
          "userId": 2,
          "commentId": 46,
          "postId": 5,
          "createdAt": "2020-08-21T16:40:47.205Z",
          "updatedAt": "2020-08-21T16:40:47.205Z",
          "author": {
            "username": "barnowl",
            "gravatar": null,
            "bio": null
          }
        }
      ],
      "author": {
        "username": "blankman2",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
     {
      "id": 47,
      "comment_body": "fsfsfsfsfsf",
      "gifUrl": "",
      "userId": 1,
      "postId": 5,
      "createdAt": "2020-08-18T04:46:08.946Z",
      "updatedAt": "2020-08-18T04:46:08.946Z",
      "commentReplies": [
        {
          "id": 18,
          "replyBody": "fsfsffsffsffsf",
          "userId": 2,
          "commentId": 46,
          "postId": 5,
          "createdAt": "2020-08-21T16:40:47.205Z",
          "updatedAt": "2020-08-21T16:40:47.205Z",
          "author": {
            "username": "barnowl2",
            "gravatar": null,
            "bio": null
          }
        }
      ],
      "author": {
        "username": "blankman3",
        "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN",
        "bio": null
      }
    },
    
  ],
  "RePosts": [],
  "RepostedByMe": false
}]

const findUsers = arr.reduce((acc, cv) => {
  const users = [
    ...acc,
    cv.author.username, // get first level author.username
    ...cv.Comments.reduce((acc, cv) => [...acc, cv.author.username], []), // get comments author.username 
    ...cv.Comments.reduce((acc, cv) => [...acc, ...cv.commentReplies.reduce((acc, cv) => [...acc, cv.author.username], [])], []) // get comment replies author.username

  ]
  return users
}, [])

console.log(findUsers)

Upvotes: 1

Unmitigated
Unmitigated

Reputation: 89264

You can use several nested flatMap and map operations.

const arr = [{ "id": 5, "title": "buttercup", "postContent": "dsdsfsfsfsfsfs", "likedByMe": false, "likeCounts": 0, "userId": 1, "createdAt": "2020-08-17T03:41:16.749Z", "updatedAt": "2020-08-17T03:41:16.749Z", "author": { "username": "blankman", "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", "bio": null }, "Likes": [], "Comments": [{ "id": 46, "comment_body": "fsfsfsfsfsf", "gifUrl": "", "userId": 1, "postId": 5, "createdAt": "2020-08-18T04:46:08.946Z", "updatedAt": "2020-08-18T04:46:08.946Z", "commentReplies": [{ "id": 18, "replyBody": "fsfsffsffsffsf", "userId": 2, "commentId": 46, "postId": 5, "createdAt": "2020-08-21T16:40:47.205Z", "updatedAt": "2020-08-21T16:40:47.205Z", "author": { "username": "barnowl", "gravatar": null, "bio": null } }], "author": { "username": "blankman2", "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", "bio": null } }, { "id": 47, "comment_body": "fsfsfsfsfsf", "gifUrl": "", "userId": 1, "postId": 5, "createdAt": "2020-08-18T04:46:08.946Z", "updatedAt": "2020-08-18T04:46:08.946Z", "commentReplies": [{ "id": 18, "replyBody": "fsfsffsffsffsf", "userId": 2, "commentId": 46, "postId": 5, "createdAt": "2020-08-21T16:40:47.205Z", "updatedAt": "2020-08-21T16:40:47.205Z", "author": { "username": "barnowl2", "gravatar": null, "bio": null } }], "author": { "username": "blankman3", "gravatar": "https://lh3.googleusercontent.com/a-/AOh14GiZAw_0UZmt7X0pJFSIv6ELQMd2nN41v0a-eKrN", "bio": null } }, ], "RePosts": [], "RepostedByMe": false }];
const res = arr.flatMap(x => [x.author.username].concat(x.Comments.flatMap(y => y.commentReplies.map(z => z.author.username).concat(y.author.username))));
console.log(res);

Upvotes: 2

Related Questions