將JSON一維數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為樹形數(shù)據(jù)結(jié)構(gòu)是編程中的基本算法,這里總結(jié)一下,當然,也參考了一些前人的經(jīng)驗。
復習一下傳址賦值(又叫引用賦值)
研究轉(zhuǎn)化方法之前,先了解一下傳址賦值,它是解決JSON結(jié)構(gòu)轉(zhuǎn)化的一個前提,如果沒有傳址賦值這種特性,咱們的方法就復雜多了。
看代碼:
var a = {
b: {
c: 1
}
}
var x = a.b
a.b.c = 2
console.log(x) // {c: 2}
可以看出,表面上var x = a.b在前,a.b.c = 2重賦值在后,但是在前賦值的變量確實會被在后賦值的代碼所影響。道理很簡單,當一個變量的值為數(shù)組或?qū)ο蟮臅r候,變量的值并不會在內(nèi)存中克隆一份,而是直接指向數(shù)組或者對象的內(nèi)存原地址,只要原數(shù)據(jù)有變化,變量也就等于有變化了。下面是數(shù)組的例子,你以為x是永遠不變的[2,3]么?并不是:
var a = [1,[2,3],4]
var x = a[1]
a[1][1] = 6
console.log(x) // [2, 6]
JSON一維結(jié)構(gòu)轉(zhuǎn)樹形結(jié)構(gòu)的要求
首先先明確一維結(jié)構(gòu)的寫法,比如,讓你將某國軍隊的從屬關(guān)系存入數(shù)據(jù)表,你怎么存?
比較普遍的寫法是:
var jsonData = [{
"id": "1",
"pid": "0",
"name": "第一戰(zhàn)區(qū)"
}, {
"id": "4",
"pid": "1",
"name": "第1軍"
}, {
"id": "5",
"pid": "4",
"name": "第1師"
}]
也就是說,使用pid標記上級,這樣一級級的追溯,最終可以形成樹狀結(jié)構(gòu),大致如下:
var jsonData = [{
"id": "1",
"pid": "0",
"name": "第一戰(zhàn)區(qū)",
"children": [
{
"id": "4",
"pid": "1",
"name": "第1軍",
"children": [
{
"id": "5",
"pid": "4",
"name": "第1師"
}
]
}
]
}]
思路
- 利用臨時數(shù)組變量,將無序的JSON數(shù)據(jù)編制序列。
因為默認的JSON結(jié)構(gòu),你無法迅速定位一個想要的元素,如果用臨時數(shù)組變量,將元素按照id存入數(shù)組,就可以快速定位元素。 - 遍歷JSON的每一個元素,去臨時變量里尋找它的父元素,如果找到,就填到這個父元素的children里面,如果找不到父元素,說明它就是一級元素。
最終將所有一級元素push到result變量即可。
這里就涉及到一個問題,就是如果先發(fā)生了一級元素已經(jīng)push到result變量,那么之后再發(fā)生下級元素填到該一級元素的children里,還來得及么?
這里就用到JS的傳址賦值的特點了,所以答案是:來得及。
代碼
代碼的JSON使用了別人的一個例子,而函數(shù)是我自己寫的:
var jsonData = [{
"id": "1",
"pid": "0",
"name": "家用電器"
}, {
"id": "4",
"pid": "1",
"name": "大家電"
}, {
"id": "5",
"pid": "1",
"name": "生活電器"
}, {
"id": "2",
"pid": "0",
"name": "服飾"
}, {
"id": "3",
"pid": "0",
"name": "化妝"
}, {
"id": "7",
"pid": "4",
"name": "空調(diào)"
}, {
"id": "8",
"pid": "4",
"name": "冰箱"
}, {
"id": "9",
"pid": "4",
"name": "洗衣機"
}, {
"id": "10",
"pid": "4",
"name": "熱水器"
}, {
"id": "11",
"pid": "3",
"name": "面部護理"
}, {
"id": "12",
"pid": "3",
"name": "口腔護理"
}, {
"id": "13",
"pid": "2",
"name": "男裝"
}, {
"id": "14",
"pid": "2",
"name": "女裝"
}, {
"id": "15",
"pid": "7",
"name": "海爾空調(diào)"
}, {
"id": "16",
"pid": "7",
"name": "美的空調(diào)"
}, {
"id": "19",
"pid": "5",
"name": "加濕器"
}, {
"id": "20",
"pid": "5",
"name": "電熨斗"
}];
function flat2tree(jsonData){
var result = [], temp = {}, i = 0, j = 0, len = jsonData.length
for(; i < len; i++){
temp[jsonData[i]['id']] = jsonData[i] // 以id作為索引存儲元素,可以無需遍歷直接定位元素
}
for(; j < len; j++){
var currentElement = jsonData[j]
var tempCurrentElementParent = temp[currentElement['pid']] // 臨時變量里面的當前元素的父元素
if (tempCurrentElementParent) { // 如果存在父元素
if (!tempCurrentElementParent['children']) { // 如果父元素沒有chindren鍵
tempCurrentElementParent['children'] = [] // 設(shè)上父元素的children鍵
}
tempCurrentElementParent['children'].push(currentElement) // 給父元素加上當前元素作為子元素
} else { // 不存在父元素,意味著當前元素是一級元素
result.push(currentElement);
}
}
return result;
}
console.log(flat2tree(jsonData))
更新一種代碼量極簡,但運算復雜度較高的算法
速度比上一種算法慢9倍,但是勝在代碼足夠簡單。
// jsonData 從略
function flat2tree(pid, arr) {
return arr.filter(item => item.pid === pid).map(item => ({...item, children: flat2tree(item.id, arr)}))
}