使用递归函数更新对象数组(动态映射回复评论)

Updating array of objects with recursive function (Mapping replies to comments dynamically)

我收到来自 graphql 后端的评论列表,格式如下:

[
        {
            "__typename": "Comment",
            "id": "1",
            "userId": "1",
            "postId": "1",
            "parentCommentId": null,
            "content": "test 1"
        },
        {
            "__typename": "Comment",
            "id": "2",
            "userId": "1",
            "postId": "1",
            "parentCommentId": null,
            "content": "this is a comment"
        },
        {
            "__typename": "Comment",
            "id": "34",
            "userId": "1",
            "postId": "1",
            "parentCommentId": "1",
            "content": "reply to test1"
        },
        {
            "__typename": "Comment",
            "id": "35",
            "userId": "1",
            "postId": "1",
            "parentCommentId": "34",
            "content": "nested reply to \"reply to test1\"\n\n"
        },
        {
            "__typename": "Comment",
            "id": "36",
            "userId": "1",
            "postId": "1",
            "parentCommentId": "34",
            "content": "test?"
        }
    ]

带有parentCommentId === null的评论是最高级别的评论,而parentCommentId !== null的评论是对id === parentCommentId

的评论的回复

我想将此数据结构转换为如下形式:

[{
    "__typename": "Comment",
    "id": "1",
    "userId": "1",
    "postId": "1",
    "parentCommentId": null,
    "content": "test1",
    "replies": [{
      "__typename": "Comment",
      "id": "34",
      "userId": "1",
      "postId": "1",
      "parentCommentId": "1",
      "content": "reply to test1",
      "replies": [{
        "__typename": "Comment",
        "id": "35",
        "userId": "1",
        "postId": "1",
        "parentCommentId": "34",
        "content": "reply to test1"
      }]
    }]
  },
  {
    "__typename": "Comment",
    "id": "2",
    "userId": "1",
    "postId": "1",
    "parentCommentId": null,
    "content": "this is a comment",
    "replies": []
  }
]

我有以下函数来进行数据转换:

function formatData(comments: Array < IComment > ) {
  let commentList = Array < IComment > ();

  // add comments without `parentCommentId` to the list.
  // these are top level comments.
  for (let i = 0; i < comments.length; i++) {
    if (!comments[i].parentCommentId) {
      commentList.push({ ...comments[i],
        replies: []
      });
    }
  }

  for (let i = 0; i < comments.length; i++) {
    if (comments[i].parentCommentId) {
      const reply = comments[i];
      mapReplyToComment(commentList, reply);
    }
  }


  return commentList;

  function mapReplyToComment(
    commentList: Array < IComment > ,
    reply: IComment
  ): any {
    return commentList.map((comment) => {
      if (!comment.replies) {
        comment = { ...comment,
          replies: []
        };
      }
      if (comment.id === reply.parentCommentId) {
        comment.replies.push(reply);

        return comment;
      } else {
        return mapReplyToComment(comment.replies, reply);
      }
    });
  }
}

然而,这仅适用于深入对象树的一层。所以我得到了主要评论的回复,但回复的回复没有添加到对象中。

这就是我现在得到的:

[{
    "__typename": "Comment",
    "id": "1",
    "userId": "1",
    "postId": "1",
    "parentCommentId": null,
    "content": "test1",
    "replies": [{
      "__typename": "Comment",
      "id": "34",
      "userId": "1",
      "postId": "1",
      "parentCommentId": "1",
      "content": "reply to test1"
      // -- I should have here another node of "replies"
    }]
  },
  {
    "__typename": "Comment",
    "id": "2",
    "userId": "1",
    "postId": "1",
    "parentCommentId": null,
    "content": "this is a comment",
    "replies": []
  }
]

你能指出我做错了什么并提供一些解释吗? 提前致谢

编辑:

根据@Nina Scholz 的评论,我提出了这个解决方案:

function formatData(data: Array < IComment > , root: string) {
  const temp: any = {};

  data.forEach((comment: IComment) => {
    const parentCommentId = comment.parentCommentId ? ? root;

    if (temp[parentCommentId] == null) {
      temp[parentCommentId] = {};
    }

    if (temp[parentCommentId].replies == null) {
      temp[parentCommentId].replies = [];
    }

    if (temp[comment.id] == null) {
      temp[parentCommentId].replies.push(
        Object.assign((temp[comment.id] = {}), comment)
      );
    } else {
      temp[parentCommentId].replies.push(
        Object.assign(temp[comment.id], comment)
      );
    }
  });
  return temp[root].replies;
}

您可以在一个对象的帮助下进行一次迭代,该对象保持父对子和子对父的引用。

const
    getTree = (data, root) => {
        const t = {};
        data.forEach(o =>
            ((t[o.parentCommentId] ??= {}).replies ??= []).push(
                Object.assign(t[o.id] ??= {}, o)
            )
        );
        return t[root].replies;
    },
    data = [{ __typename: "Comment", id: "1", userId: "1", postId: "1", parentCommentId: null, content: "test 1" }, { __typename: "Comment", id: "2", userId: "1", postId: "1", parentCommentId: null, content: "this is a comment" }, { __typename: "Comment", id: "34", userId: "1", postId: "1", parentCommentId: "1", content: "reply to test1" }, { __typename: "Comment", id: "35", userId: "1", postId: "1", parentCommentId: "34", content: "nested reply to \"reply to test1\"\n\n" }, { __typename: "Comment", id: "36", userId: "1", postId: "1", parentCommentId: "34", content: "test?" }],
    tree = getTree(data, null);

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

因为另一个答案对我来说很难理解,所以我也会 post 我的,它将步骤分成独立的函数: (请注意,我在您的示例数据中添加了回复数组)

let data = [{    "__typename": "Comment",    "id": "1",    "userId": "1",    "postId": "1",    "parentCommentId": null,    "content": "test 1",    "replies": []  },
  {    "__typename": "Comment",    "id": "2",    "userId": "1",    "postId": "1",    "parentCommentId": null,    "content": "this is a comment",    "replies": []  },
  {    "__typename": "Comment",    "id": "34",    "userId": "1",    "postId": "1",    "parentCommentId": "1",    "content": "reply to test1",    "replies": []  },
  {    "__typename": "Comment",    "id": "35",    "userId": "1",    "postId": "1",    "parentCommentId": "34",    "content": "nested reply to \"reply to test1\"\n\n",    "replies": []  },
  {    "__typename": "Comment",    "id": "36",    "userId": "1",    "postId": "1",    "parentCommentId": "34",    "content": "test?",    "replies": []  }
]

function findLowestComment(dataArray) {
  for (let i = 0; i < dataArray.length; i++) {
    let comment = dataArray[i]
    isLowest = true
    if (comment.parentCommentId == null) {
      continue
    }
    for (let j = 0; j < dataArray.length; j++) {
      if (dataArray[j].id != comment.id &&
        dataArray[j].parentCommentId == comment.id &&
        dataArray[j].parentCommentId != null) {
        isLowest = false;
        break
      }
    }
    if (isLowest) {
      return i
    }
  }
}

function insertIntoParent(dataArray, commentIndex) {
  for (let j = 0; j < dataArray.length; j++) {
    if (dataArray[j].id == dataArray[commentIndex].parentCommentId) {
      dataArray[j].replies.push(dataArray[commentIndex])
      dataArray.splice(commentIndex, 1)
      break
    }
  }
}

function mapComments(dataArray) {
  for (let j = 0; j < dataArray.length; j++) {
    let lowestIndex = findLowestComment(dataArray)
    insertIntoParent(dataArray, lowestIndex)
  }
}

mapComments(data)
console.log(JSON.stringify(data, undefined, 2))