The question I'm going to ask is about Pinia, but really could just be generalized to any underlying store.
I have a Vue + Pinia application in which I want to be able to store a tree. The tree is made up of objects of type Node
. I need to store exactly one tree at a time, and I don't care about the root (we can imagine it's there, but what I care about is the root's children, their children, and so on).
I want to support the following operations:
Here's something I have thought of doing:
Node
s containing the top level nodes. Let's call it topLevelNodes
nodeIdToChildren
, which maps the id of a node to an array of Node
s that are its childrenI would initially fetch the top level nodes, filling the array topLevelNodes
.
For each node that needs to know its children, fetch them and put them in nodeIdToChildren
under the parent id as key.
An advantage of this approach is that it's easy to add, delete, and move around nodes: just touch the relevant entries in the mapping. The biggest drawback is that it's much less efficient to just find a node, regardless of its position. Say I want to edit node with id xyz
, not knowing whose child it is.
I could create a getter that flattens all the values in the mapping object together with the values in the top level nodes array, but I'm not sure about efficiency of that.
Are there any better ways of doing this?
The most efficient way is to store all nodes in a single array and in the children/parents array of a node you only place ids of other nodes.
parents
, but it returns the ids of all items which currently contain the current item's id in their children array. Similarly, if you store parents, the children will be a computed.Notes :
getItemDetails
))children
array of ids, add its ID to the new parent's children
. The child itself is not affected, except the value of its parents
computed changes. If you're storing parents
, - and children
is computed - you need to change the child's parents
array. And, of course, each of the old and new parent's children
computed will change when you perform this change). You can see a working demo here . Sorry about the styles, I just tweaked something from a different sandbox. But you get the basic implementations of both <TreeView />
and <TableView />
. This one has 1k nodes but should have no issues up to 5k nodes. Above that, you need to be careful what you render and when.
This is not specified to Vue/Pinia so I just write the general idea, you can choose the way to implement it.
You will store your tree on a flat map.
const tree = new Map()
Each item will be a node
and the key is the nodeId
. Each node will contain these properties:
{
id: "the node id",
parentId: "id of the parent, null if it is the root node",
childIds: "array of the id of its child",
content: "content of the node, whatever you want"
}
Let's go through each operator you want:
// difficulty: easy
const rootNode = {
id: "nodeId"
parentId: null,
childIds: [...],
content: "..."
}
tree.set(nodeId, rootNode)
// difficulty: easy
// add the child first
const childNode = {
id: "nodeId"
parentId: "parentId",
childIds: [...],
content: "..."
}
tree.set(nodeId, childNode)
// add the child id to the parent node
const parentNode = tree.get(parentId)
parentNode.childIds.push(childNode.id)
// set the parent back to your tree
tree.set(parentNode.id, parentNode)
// modify a node.
// difficulty: easy
const node = tree.get(nodeId)
// ... make the modification
// set it back to the tree
tree.set(nodeId, node)
// delete a node.
// difficulty: medium
// retrieve the node first
const node = tree.get(nodeId)
// delete it
tree.delete(nodeId)
// delete all of its children
// you need a recursive delete function here to go through all of the node child and child of child and so on
tree.childIds.forEach(recursiveDelete)
// don't forget to delete the nodeId from its parent node. It's easy
...
// moving around the tree is quite easy, you just need to follow the `parentId` and `childIds`
// changing a node's parent (same level)
// difficulty: easy
// you just need to change the parentId of the node. And modify the childIds of its old and new parent
// changing a node level, moving its children accordingly
// difficulty: easy
// same as changing a node parent above. Its children will move accordingly
// changing a node level to be a child of one of its children
// difficulty: hard
// get the node
const node = tree.get(nodeId)
// go through its children and update the parentId of each to the node.parentId (moving its children to be the direct child of its parent)
node.childIds.forEach((childId)=> updateParentId(childId, node.parentId))
// set the node parentId to the new one
node.parentId = newParentId
// set new childIds for the node if you want
node.childIds = [...]
// don't forget to set it back on the tree
tree.set(node.id, node)
// There is no problem at all. You just need to load from the root
Pros and Cons
Pros:
Cons
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.