1. 程式人生 > 其它 >js中操作樹結構的資料

js中操作樹結構的資料

技術標籤:javascriptjavascript

現在前端操作樹的資料結構還是挺常見的,我在這裡總結一下js樹形結構常見的操作方式。我也把它做成了工具類庫,github地址

一.遍歷樹結構的方式

假設現在有如下的資料結構,我們需要去找到樹結構的中某個物件的資料,常見的操作方式應該是用遞迴進行遍歷查詢,我認為這是最基本應該能想到的。

let tree = [
  {
    id: '1',
    title: '節點1',
    children: [
      {
        id: '1-1',
        title: '節點1-1'
      },
      {
id: '1-2', title: '節點1-2' } ] }, { id: '2', title: '節點2', children: [ { id: '2-1', title: '節點2-1' } ] } ]
const recursion = (cityData, id) => {
      if (!cityData || !cityData.length) {
        return
      }
      //先循壞cityData
for (let i = 0; i < cityData.length; i++) { const childs = cityData[i].children; // console.log(cityData[i].id) if (cityData[i].id === id) { result = cityData[i] } if (childs && childs.length > 0) { recursion(childs, id); }
} return result } console.log(recursion(cityData, "2-1"))// {title:"節點2-1",id:"2-1"}

也可以使用棧迭代的方式,這也是我個人平時遍歷樹常用的一種方式。

let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定義一個數據棧
  let stack = [];

  let item = null;

  //先將第一層節點放入棧
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 將資料棧的第一個取出來
    item = stack.shift();
    // 如果符合就賦值給result
    if (item.id === id) {
      result = item
    }
    //如果該節點有子節點,繼續新增進入棧底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

console.log(range(cityData, "2-1"))// {title:"節點2-1",id:"2-1"}

二.操作樹的增刪改查

1.列表與樹的相互裝換
列表的資料結構會在節點資訊中給定當前父元素的pid,然後可以通過pid進行相互關聯進而轉換成為一棵樹結構。

let list = [
      {id: "1",title: "節點1",parentId: "",},
      {id: "1-1",title: "節點1-1",parentId: "1"},
      {id: "2",title: "節點2",parentId: ""},
      {id: "2-1", title: "節點2-1", parentId: "2"},
      {id: "1-2", title: "節點1-2", parentId: "1"},
      {id: "1-3", title: "節點1-3", parentId: "1"},
      {id: "2-3", title: "節點2-3", parentId: "2"},
      {title: "節點2-3-1", parentId: "2-3",id: "2-3-1"}
    ];

在tree.js中

class Tree {

  constructor(config = {}) {
    this.defaultConfig = {
      id: "id",
      children: "children",
      pid: "pid"
    };
    this.config = Object.assign(this.defaultConfig, config);
  }

  // list=>tree 列表轉換成為樹
  listToTree(list) {
    let info = list.reduce((map, node) => {
      if (!map[node[this.config.id]]) {
        map[node[this.config.id]] = node;
        node.children = [];
      }
      return map;
    }, {});
    return list.filter(v => {
      if (info[v[this.config.pId]]) {
        info[v[this.config.pId]].children.push(v);
      }
      return !v[[this.config.pId]];
    });
  }
}

export default Tree;

這裡我們利用了物件key的唯一性,把info與當前元素id建立起來一個對映關係,那麼它映射出來的結構如下:
在這裡插入圖片描述
然後通過filter遍歷list中的元素,判斷當前當前元素的pid是否存在於info集合中,如果存在,則說明當前元素為info中某項的子元素。然後再過濾出pid為空的元素。

呼叫:

let list = [
      {
        id: "1",
        title: "節點1",
        parentId: "",
      },
      {
        id: "1-1",
        title: "節點1-1",
        parentId: "1"
      },

      {
        id: "2",
        title: "節點2",
        parentId: ""
      },
      {id: "2-1", title: "節點2-1", parentId: "2"},
      {id: "1-2", title: "節點1-2", parentId: "1"},
      {id: "1-3", title: "節點1-3", parentId: "1"
      },
      {id: "2-3", title: "節點2-3", parentId: "2"},
      {title: "節點2-3-1", parentId: "2-3", id: "2-3-1"}
    ];
    console.log(new Tree({id: "id", pId: "parentId", children: "children"}).listToTree(list))

2.樹轉換成為列表

treeToList(tree) {
    const { children } = this.config; const result = [...tree];
    for (let i = 0; i < result.length; i++) {
      if (!result[i][children]) {continue;}
      result.splice(i + 1, 0, ...result[i][children]);
    }
    return result;
  }

樹變成列表你可以這樣理解,遍歷當前項資料,判斷children.length是否大於0,如果大於則把它的children中的每個元素都排進列表中即可,由於此時result長度發生了變化,那麼它的迴圈也會繼續走下去。如果children.length === 0 則說明他沒有子節點則不需要任何的操作。