Javascript 将数据解析为可映射嵌套列表| React
我正在建立一个数据库,您可以在其中添加对会议的评论和对这些会议的回复。我从中获得灵感,因为我以前从未做过这样的事情 我正在从数据库中获取我的评论,结构如下所示:Javascript 将数据解析为可映射嵌套列表| React,javascript,reactjs,algorithm,sorting,Javascript,Reactjs,Algorithm,Sorting,我正在建立一个数据库,您可以在其中添加对会议的评论和对这些会议的回复。我从中获得灵感,因为我以前从未做过这样的事情 我正在从数据库中获取我的评论,结构如下所示: "comments": [ { "id": 5, "content": "First comment", "path": [ 5
"comments": [
{
"id": 5,
"content": "First comment",
"path": [
5
],
"depth": 1
},
{
"id": 6,
"content": "First reply on first comment",
"path": [
5,
6
],
"depth": 2
},
{
"id": 7,
"content": "Second comment",
"path": [
7
],
"depth": 1
},
{
"id": 8,
"content": "First reply on second comment",
"path": [
7,
8
],
"depth": 2
},
{
"id": 9,
"content": "Second reply on second comment",
"path": [
7,
9
],
"depth": 2
},
{
"id": 10,
"content": "First reply on second comment second reply",
"path": [
7,
9,
10
],
"depth": 3
}
]
目前,我正在尝试使用React应用程序创建一个嵌套列表,并以与StackOverflow类似的方式显示注释。我对该结构的外观有一个想法,但我不太确定如何实现它
comments: [
{
id: 5,
content: "First comment",
replies: [
{
id: 6,
content: "First reply on first comment",
depth: 2
}
]
},
{
id: 100,
content: "Comment with no replies",
replies: []
}
]
非常感谢您对如何开始这方面的任何帮助,我希望我已经清楚地描述了我的问题
感谢阅读,祝您度过愉快的一天。我要做的第一件事是在每个项目中添加
parentId
、children
和order
。这将允许您上下遍历树,无论从哪个节点开始,并确保正确的顺序。parentId
是不言自明的,而childrenid
可能应该命名为childrenIds,因为它只是按照适当的顺序保存子项的id
然后,我通常喜欢将数组转换为一个映射,该映射由一个唯一的值(如ID)进行键控。这将为您提供一个O(1)操作,以访问任何可能被编辑或删除的节点。我喜欢使用Ramda来处理类似的事情,但您可以使用任何其他实用程序或编写自己的实用程序
utils.js
现在,您需要找到列表顶部的注释,或者如果您有注释,则位于“根”下方。因为我选择了使用parentId:null
来表示这一点,所以我们可以找到这些顶级项
const getTopLevelParents = R.filter(x => !x.parentId);
有了这一点,我们可以很容易地让顶级项目开始迭代。这可能会变得更复杂一些。我在这里也使用Ramda,所以我希望它不会太不寻常。要点是,从顶级项中的第一项开始,我们递归地查找子项,按“顺序”排序,设置深度,并将该项数组连接到单个数组。因此,结果是更线性和更有序的。因为当我们将这些数据映射到组件时,我们不想为了找到父对象和子对象而跳过。我们希望能够从上到下,已经解决了所有这些问题
/**
* Converts the mapping to an array and reorders the children so they are directly adjacent with the depth set.
* @param treeMap Key value pair where the key is the ID and the value is the chapter
*/
function getTreeMapAsList(treeMap) {
const topLevel = R.compose(
getTopLevelParents,
R.values
)(treeMap);
/**
* Nest this function so it can have access to treeMap.
*/
function getTree(data, depth = 0) {
return R.reduce(
(result, curr) => {
result.push(curr);
// hasLength is a utility that just checks if the length of an
// item is greater than 0 in a way that won't blow up if something
// doesn't exist.
if (hasLength(R.prop("children", curr))) {
const children = R.compose(
R.sortBy(R.prop("order")),
R.reduce((result, id) => {
const child = R.prop(id, treeMap);
if (child) {
return R.compose(
R.append(R.__, result),
R.assoc("depth", depth + 1)
)(child);
}
return result;
}, [])
)(curr.children);
return result.concat(getTree(children, depth + 1));
}
return result;
},
[],
data
);
}
现在,该树已正确排序,具有适当的深度,并已展平为一个数组。最后,您可以使用注释组件映射此数组
Comment.js
函数注释({data}){
返回(
{
返回;
})}
);
}
。祝你好运 我要做的第一件事是在每个项目中添加parentId
、children
和order
。这将允许您上下遍历树,无论从哪个节点开始,并确保正确的顺序。parentId
是不言自明的,而childrenid
可能应该命名为childrenIds,因为它只是按照适当的顺序保存子项的id
然后,我通常喜欢将数组转换为一个映射,该映射由一个唯一的值(如ID)进行键控。这将为您提供一个O(1)操作,以访问任何可能被编辑或删除的节点。我喜欢使用Ramda来处理类似的事情,但您可以使用任何其他实用程序或编写自己的实用程序
utils.js
现在,您需要找到列表顶部的注释,或者如果您有注释,则位于“根”下方。因为我选择了使用parentId:null
来表示这一点,所以我们可以找到这些顶级项
const getTopLevelParents = R.filter(x => !x.parentId);
有了这一点,我们可以很容易地让顶级项目开始迭代。这可能会变得更复杂一些。我在这里也使用Ramda,所以我希望它不会太不寻常。要点是,从顶级项中的第一项开始,我们递归地查找子项,按“顺序”排序,设置深度,并将该项数组连接到单个数组。因此,结果是更线性和更有序的。因为当我们将这些数据映射到组件时,我们不想为了找到父对象和子对象而跳过。我们希望能够从上到下,已经解决了所有这些问题
/**
* Converts the mapping to an array and reorders the children so they are directly adjacent with the depth set.
* @param treeMap Key value pair where the key is the ID and the value is the chapter
*/
function getTreeMapAsList(treeMap) {
const topLevel = R.compose(
getTopLevelParents,
R.values
)(treeMap);
/**
* Nest this function so it can have access to treeMap.
*/
function getTree(data, depth = 0) {
return R.reduce(
(result, curr) => {
result.push(curr);
// hasLength is a utility that just checks if the length of an
// item is greater than 0 in a way that won't blow up if something
// doesn't exist.
if (hasLength(R.prop("children", curr))) {
const children = R.compose(
R.sortBy(R.prop("order")),
R.reduce((result, id) => {
const child = R.prop(id, treeMap);
if (child) {
return R.compose(
R.append(R.__, result),
R.assoc("depth", depth + 1)
)(child);
}
return result;
}, [])
)(curr.children);
return result.concat(getTree(children, depth + 1));
}
return result;
},
[],
data
);
}
现在,该树已正确排序,具有适当的深度,并已展平为一个数组。最后,您可以使用注释组件映射此数组
Comment.js
函数注释({data}){
返回(
{
返回;
})}
);
}
。祝你好运 我以前实现过类似的东西,但我选择将数据转换成一个由ID设置关键帧的映射/对象。我会添加一个不会被渲染的“根”,并将该对象转换为平面阵列,更类似于第一个对象的外观。然后,使用深度道具,设置边距,使其在其父项下缩进一个设置量。对我来说,这只是另一个ID,但也可能是你在那里的路径。然后是一个深度优先的树渲染算法。谢谢@jktravis的评论。我从未听说过深度优先算法,所以我不太确定如何在我的案例中实现它。也许它超级简单,但事实上,我以前从未使用过它,这让它成为嘲弄。你介意帮我一点忙吗?这听起来确实很复杂,但它实际上只是一个用于搜索或遍历树的递归算法。这才是你真正要做的。看见不过,在这里,您基本上是将数据转换为react组件。如果可以的话,我会给每一个添加一个parentId
和一个children
道具。举个例子会不会太过分?我昨天尽了最大的努力,但还是没能成功你是不是碰巧成功了@MartinNordströmI以前实现过类似的东西,但我选择将数据转换成一个由ID设置关键帧的映射/对象。我
function App() {
// This is the map and should be stored in state somewhere.
// If it's changing a lot, you should memoize it.
const treeMap = keyById(data.comments);
// This is the array, that is now ordered with appropriate depths
// This should also be memoized.
const treeList = getTreeMapAsList(treeMap);
return (
<div>
<div>
{/* Now we just have an array to iterate over. */}
{treeList.map(item => {
return <Comment data={item} key={item.id} />;
})}
</div>
</div>
);
}