1. 程式人生 > >Js 將陣列按父子關係轉換為物件樹

Js 將陣列按父子關係轉換為物件樹

這是一道遇到過兩次的前端筆試題,第一次是在哪裡記不清了,第二次是在今天上午多益網路的筆試中,兩次試題的具體內容稍微有差別,因為時間和IDE 的原因都沒能解出正確答案。事後又花點時間搗鼓一下,整理出兩種解法,記錄在這裡。

1. 問題的輸入與輸出

首先來看一下這個問題的輸入和輸出:

// 輸入
var nodes = [
  {id: 10, title: 'dw10', parentId: 4},
  {id: 2, title: 'dw2', parentId: 0},
  {id: 4, title: 'dw4', parentId: 2},
  {id: 12, title: 'dw12', parentId: 2},
  {id: 8, title: 'dw8', parentId: 4}
];
// 輸出
{
  "id": 2,
  "title": "dw2",
  "parentId": 0,
  "children": [
    {
      "id": 12,
      "title": "dw12",
      "parentId": 2
    },
    {
      "id": 4,
      "title": "dw4",
      "parentId": 2,
      "children": [
        {
          "id": 8,
          "title": "dw8",
          "parentId": 4
        },
        {
          "id": 10,
          "title": "dw10",
          "parentId": 4
        }
      ]
    }
  ]
}

2. 解法一:廣度優先搜尋

第一種解法的步驟如下:

(1)首先,按照每一項的parentId 數值給輸入陣列排序。

(2)將排序後的陣列的第一條資料作為根元素,遍歷陣列,比較當前記錄的parentId 和結果物件中的id 依次將剩下的資料插入到合適的位置上,在尋找合適位置的過程中使用的是廣度優先搜尋的方法。

此種方法的時間複雜度較高,程式碼量頗大,實現程式碼如下:

var nodes = [
  {id: 10, title: 'dw10', parentId: 4},
  {id: 2, title: 'dw2', parentId: 0},
  {id: 4, title: 'dw4', parentId: 2},
  {id: 12, title: 'dw12', parentId: 2},
  {id: 8, title: 'dw8', parentId: 4}
];

function findPos(obj, id) {
  // 廣度優先搜尋遍歷當前物件
  var queue = [], temp;
  if(obj.id === id) {
    return obj;
  }
  queue.push(obj.children);
  while(queue) {
    temp = queue.shift();
    var res = findChildIndex(id, temp);
    if(res) {
      return res;
    }
    for(var i = 0; i < temp.length; i++) {
      queue.push(temp[i]);
    }
  }
}

// 判斷一個數字是否在children 列表內
function findChildIndex(id, arr) {
  if(arr.id && id === arr.id) {
    return arr;
  }
  for(var i = 0; i < arr.length; i++) {
    if(id === arr[i].id) {
      return arr[i];
    }
  }
  return null;
}

// sort
function cmp(a, b) {
  return a.parentId - b.parentId;
}
nodes.sort(cmp);

var resObj = nodes[0];
for(var i = 1, len = nodes.length; i < len; i++) {
  var findRes = findPos(resObj, nodes[i].parentId);
  if(findRes) {
    if(!findRes.children) {
      findRes.children = [];
    }
    findRes.children.push(nodes[i]);
  }
}
console.log(resObj);

3. 解法二:從後向前

在控制時間複雜度的情況下,上面提到的方法就無法達到要求。於是又開始思考第二種方法,排序後從後向前遍歷陣列。如果把最終的物件想象成一顆樹的話,這個方法就相當於從葉子節點開始到根節點逐漸建立一棵完整的樹。

與上一個方法相比,該方法時間複雜度較低,且程式碼量不大,實現程式碼如下:

var nodes = [
  {id: 10, title: 'dw10', parentId: 4},
  {id: 2, title: 'dw2', parentId: 0},
  {id: 4, title: 'dw4', parentId: 2},
  {id: 12, title: 'dw12', parentId: 2},
  {id: 8, title: 'dw8', parentId: 4}
];

// sort
function cmp(a, b) {
  return a.parentId - b.parentId;
}
nodes.sort(cmp);

var midObj = {};
// 從後向前遍歷
for(var i = nodes.length - 1; i >= 0; i--) {
  var nowPid = nodes[i].parentId;
  var nowId = nodes[i].id;
  // 建立當前節點的父節點的children 陣列
  if(midObj[nowPid]) {
    midObj[nowPid].push(nodes[i]);
  } else {
    midObj[nowPid] = [];
    midObj[nowPid].push(nodes[i]);
  }
  // 將children 放入合適的位置
  if(midObj[nowId]) {
    nodes[i].children = midObj[nowId];
    delete midObj[nowId];
  }
}

console.log(midObj[0][0]);