使用递归函数更新对象数组(动态映射回复评论)
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))
我收到来自 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))