In the project encountered tree data processing display and screening, think it is necessary to record this idea and processing method, which also appeared some deep copy related knowledge, write down to deepen the impression, prevent forgetting oh.
1. Introduction of tree data
First of all, what is tree data? This is actually a name I don’t know where, not authoritative, in fact, when a data needs to be represented hierarchically, you need to use the form of tree data. For example:
This is a data to describe the classification of courses, the top layer of data is obviously name from “associations and associations” to “school student union organization”, you can see their IDS from 1-4, and then their PID is 0, meaning they have no parent node.
And then we go down to the next level, so the ids are from 5 to 8, and then their Pids are from 1 to 4, so what does that mean? In other words, their parent nodes are data with IDS 1-4 respectively. That is, societies and associations with ID =1 have a child node under student Studio with ID =5 (because student Studio has pid=1), and then student Studio with ID =5 must also have other children with PID =5.
To this should be able to understand the concept of tree data bar, that is, each database data not only has its own ID, but also his parent node ID (PID), so that it can be convenient and clear to express the hierarchical relationship between the father and child!
2. Display of tree data
So once we have this data, how do we present it in a hierarchical form?
We can use the Table component inside the Element component, as shown below:
We clicked on it to see how it worked, and found that it needed an array of the form shown here:
Obviously, it does not conform to the data format we received. He needs to install the child nodes of each data in the children array, instead of using PID to represent the relationship between the father and the child. Therefore, we need to start to convert the original flat data into real tree data (with multiple layers).
So we need to write an algorithm to do that, recursively, obviously. The code is as follows:
export function filterGroupClassificationList(data)
{
//father_id specifies the id of the father at each level
let filter = (father_id) = > {
let array = []
// Get the parent id and go through the data
// see whose parentId is the parentId
data.forEach(item= > {
if (item.parentId === father_id) {
// Store all the sons of the parent id in an array
array.push(item)
}
})
// Go through all the sons again
array.forEach(item= > {
// Pass the son's id recursively, then go to the son's son
let temp = filter(item.id)
// If the son's son is not empty
// put the son's son into the son's children object
if(temp.length ! =0) {
item.children = temp
}
})
// Then return the resulting array
return array
}
// Pass in the parentId of the first layer
let filterArray = filter(0)
// Finally return the filtered data
return filterArray
}
Copy the code
I believe I have written this note is very clear, the algorithm is good brother want to come out, I streamlined, good brother really fierce!
But not only that, because there is a sort field in the data, and we have only changed the data into a tree, but not sorted, so we need to write a sort function to sort the tree, which is also recursive, as follows:
let sortWay = array= > {
// Sort the current array (sort)
array.sort(function(a, b) {(a.ort - b.ort means sort from smallest to largest)return a.sort - b.sort
})
// Then iterate through the current array and sort their sons if they exist
array.forEach(item= > {
item.children && sortWay(item.children)
})
// Returns the sorted array
return array
}
Copy the code
So put them together and the code looks like this:
export function filterGroupClassificationList(data)
{
//father_id specifies the id of the father at each level
let filter = (father_id) = > {
let array = []
// Get the parent id and go through the total data
// see whose parentId is the parentId
data.forEach(item= > {
if (item.parentId === father_id) {
// Store all the sons of the parent id in an array
array.push(item)
}
})
// Go through all the sons again
array.forEach(item= > {
// Pass the son's id recursively, then go to the son's son
let temp = filter(item.id)
// If the son's son is not empty
// put the son's son into the son's children object
if(temp.length ! =0) {
item.children = temp
}
})
// Then return the resulting array
return array
}
let sortWay = array= > {
// Sort the current array (sort)
array.sort(function(a, b) {(a.ort - b.ort means sort from smallest to largest)return a.sort - b.sort
})
// Then iterate through the current array and sort their sons if they exist
array.forEach(item= > {
item.children && sortWay(item.children)
})
// Returns the sorted array
return array
}
// Pass in the parentId of the first layer
let filterArray = filter(0)
//
// Finally return filtered and sorted data
return sortWay(filterArray);
}
Copy the code
After sorting, we can introduce this function in the project and use it to filter, and get the following data:
Children = pid=3 (id=3); children = pid=3 (id=3);
Then let’s show it!
The table argument inside element is as follows:
<el-table
:data="treeData"
row-key="id"
v-loading="loading"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
Copy the code
The effect is as follows:
3. Expansion of other requirements
You think this is the end of it? Nonono, another requirement in the project is to be able to change the order of all the data in the same level. The core problem is to find all the brothers of the data (array with the same PID), and then change their order. There are several options:
1. The first one comes to my mind in a flash. Directly traverse our tree and find the data whose ID is equal to the pid of the data (that is, the father of the element), and then the father’s children object contains the information of all brothers. But that’s not going to be good, because you’re going to recursively go through the whole number, and the performance might not be very good. Pass!
2. Then I thought, why do we traverse trees? Why can’t we traverse flat data? That is, the data that wasn’t filtered at the beginning, they’re all in an array of objects, and we just walk through the array, find all the data with equal Pids, and push them into an array. It’s a lot easier than going through the numbers.
3. Then my brother came up with a way to save time and complexity, which I call wonderful. It borrowed information from the parent component of Vue, which also contains a _parent_ object to store the data of the parent. So we don’t just have to look for the children of the _parent_ object in the array to find the sibling data?
What about the data at the top? They don’t have a father?
Isn’t the data at the top level the filtered array? They don’t have a parent and just assign null to their _parent_ object.
So the key is how to add a _parent_ object to each array to store its parent node’s data? Add a parent object to the recursive algorithm, and then assign the parent object to the current data:
export function filterGroupClassificationList(data)
{
//fahter father object, father_id is the id of each hierarchy's father object
let filter = (father,father_id) = > {
let array = []
// Get the parent id and go through the data
// see whose parentId is the parentId
data.forEach(item= > {
if (item.parentId === father_id) {
// Store all the sons of the parent id in an array
array.push(item)
}
})
// Go through all the sons again
array.forEach(item= > {
// Recursively pass in the son's object and object ID, then go to the son's son
let temp = filter(item,item.id)
// If the son's son is not empty
// put the son's son into the son's children object
if(temp.length ! =0) {
item.children = temp
}
// Each data is assigned a _parent_ object whose value is their father object.
item._parent_ = father;
})
// Then return the resulting array
return array
}
// pass in the parent object and parentId(there is no parent in the first layer, so the parent object is null and the parentId is 0)
let filterArray = filter(null.0)
// Finally return the filtered data
return filterArray
}
Copy the code
We now have a parent object for each data object, _parent_, as shown below:
ParentId =3, parentId=3, parenent_ =3, parentId=3, parentId=3, parentId=3, parentId=3
4. It brings new problems
You thought it was over again? Not really. This method can save a lot of time and complexity, but it introduces new problems!
When you need to this tree array screening (such as search data is by name, which is filtered by the user in the search box input name), of course, you can let the backend written screening, you as long as the condition is good, but if in order to reduce the number of requests, we’re going to on the front end of the data filtering, We need to pass the deep copy of the array (because filtering cannot change the value of the original array) and the filter condition (the search condition) into a function and then process it. So what’s the problem? Here’s the kicker:
The problem is this deep copy! No matter how deep the copy is, it must iterate over the object, but our array with each data has its children(the lowest level has no children) and a _parent_ object (the first level has no _parent_ object) is a ring! So when you recurse, you’re going to get an error, because it’s a ring! He can’t go all the way through, because he can go all the way down from the children object, and he can go all the way up from the _parent_ object, so there’s no base for recursion! So the disadvantage of this method is that if the previous method only has children, it is free to recursively traverse the deep copy, but this method also requires that the _parent_ object is not used in the deep copy: Parse (json.stringify (data)) then add a judgment function to json.stringify as follows:
let filterArray = filterName(JSON.parse(JSON.stringify(data,((key,val) = > {
// Return recursively if key is not equal to __parent__
if(key ! ='__parent__')
return val
}))),name)
return filterArray;
Copy the code