javascript如何用遞歸寫一個簡單的樹形結構

来源:http://www.cnblogs.com/historylyt/archive/2017/09/27/7602300.html
-Advertisement-
Play Games

轉自博客園: 現在有一個數據,需要你渲染出對應的列表出來: var data = [ {"id":1}, {"id":2}, {"id":3}, {"id":4}, ]; var str="<ul>"; data.forEach(function(v,i){ str+="<li><span>"+v. ...


轉自博客園:

現在有一個數據,需要你渲染出對應的列表出來:

var data = [
  {"id":1},
  {"id":2},
  {"id":3},
  {"id":4}, 
];

var str="<ul>";

data.forEach(function(v,i){
   str+="<li><span>"+v.id+"</span></li>"
})

str="</ul>"

$(doucment).append(str);

哼,easy!

語罷,又是一道題飛來!

哦,還帶了兒子來當幫手。我一個迴圈再一個迴圈,輕鬆帶走你們

var data2 = [
    {"id":1,children:[{"id":"child11"},{"id":"child12"}]},
    {"id":2},
    {"id":3children:[{"id":"child31"},{"id":"child32"}]},
    {"id":4}, 
];


var str="<ul>";

data2.forEach(function(v,i){
    if(v.children&&v.children.length>0){
        str+="<li><span>"+v.id+"</span>";
        str+="<ul>";
        v.children.forEach(function(value,index){
             str+="<li><span>"+value.id+"</span>";
        })
        str="</ul>";
        str="</li>";

    }else{
        str+="<li><span>"+v.id+"</span></li>"
    }  
})
str="</ul>"

$(doucment).append(str);

還有誰?

var json=[
        {
          name:123,id:1
          children:[
            {
              name:453,id:456,children:[{name:789,id:777,children:[{name:"hahahqqq---qq",id:3232,children:[name:'son',id:"13132123211"]}]}]
            },
            {
              name:"Cessihshis" , id:12121
            }
          ]
        },
        {
          name:"啊啊啊11", id:12
        },
      ];

竟然把全家都帶來了,看我迴圈迴圈再迴圈大法。

嗯,不知道他家幾代同堂,我該迴圈幾次?突然間你感覺遇到對手了。

正納悶著,突然有人拍了一下你的肩膀,兄弟,我這裡有一本遞歸秘籍,我看你骨骼驚奇,是個練武奇才,10塊錢賣你了。

  function render(treeJson){   
    if(!Array.isArray(treeJson)||treeJson.length<=0){return ""}   
    var ul=$("<ul>");
    treeJson.forEach(function(item,i){      
      var li=$("<li><span class='treeName'>"+item.name+"</span></li>");
      if(Array.isArray(item.children)&&item.children.length>0){
        li.append(render(item.children))
      }
      ul.append(li);
    })
    return ul
  }

  $(document).append(render(json));

好了不扯了,通過遞歸,無需再判斷數據有多少層級,只有當前數組有children並且長度大於0,函數就會遞歸調用自身,並且返回一個ul。

這樣一來,一顆非常簡陋的樹就生成了,不過通常樹都帶有radio或者checkbox選擇框,而且很多時候都需要對樹的右側進行拓展,比如加一些新增,編輯等按鈕什麼的,可以考慮多傳一個對象作為參數。

      var checkbox={
        radio:"<label class='myTreeIcon'><input type='radio'  name='selectTreeRedio'><span></span></label>",

        multi:"<input type='checkbox' name='selectTreeRedio'>"
      }


      function render(treeJson,option={type:0,expandDom:function(){}}){   
         if(!Array.isArray(treeJson)||treeJson.length<=0){return ""}   
          var {type,expandDom}=option;        
          var ul=$("<ul>");
          treeJson.forEach(function(item,i){
            var str="";
            if(type==1){
              str+=checkbox.multi
            }else if(type==2){
              str+=checkbox.radio
            }
            var li=$("<li data-id='"+item+"'>"+str+"<span class='treeName'>"+item.name+"</span></li>");
            expandDom&&expandDom(li,item);
            if(item.children&&item.children.length>0){
              li.append(render(item.children,option))
            }
            ul.append(li);
        })
        return ul
      }

      //option使用了一個預設對象,預設為不需要選擇框和不需要拓展, 如果傳入的type為1或者2,則生成checkbox或者radio,由於radio樣式比較醜,用label包起來自己模擬選中的效果;如果傳入拓展參數,則把當前的父級li以及當前的參數傳入,以便進行拓展。


      $("#tree").append(render(json,{
        type:1,
        expandDom:function(el,data){
          el.append("<button>編輯</button><button>測試</button><a data-msg='"+JSON.stringify(data)+"'></a>")
        }
      }))

有時候後臺返回的可能不是拼裝好層級的數組,而是帶有pid標識的所有數組的集合,比如:

var data = [
  {"id":2,"name":"第一級1","pid":0},
  {"id":3,"name":"第二級1","pid":2},
  {"id":5,"name":"第三級1","pid":4},
  {"id":100,"name":"第三級2","pid":3},
  {"id":6,"name":"第三級2","pid":3},
  {"id":601,"name":"第三級2","pid":6},
  {"id":602,"name":"第三級2","pid":6},
  {"id":603,"name":"第三級2","pid":6}
];

為了用遞歸來渲染出樹來,這時,就需要我們手動來將層級裝好了:

   function arrayToJson(treeArray){
    var r = [];
    var tmpMap ={};

    for (var i=0, l=treeArray.length; i<l; i++) {
     // 以每條數據的id作為obj的key值,數據作為value值存入到一個臨時對象裡面
      tmpMap[treeArray[i]["id"]]= treeArray[i]; 
    } 

    for (i=0, l=treeArray.length; i<l; i++) {
      var key=tmpMap[treeArray[i]["pid"]];
      
      //迴圈每一條數據的pid,假如這個臨時對象有這個key值,就代表這個key對應的數據有children,需要Push進去
      if (key) {
        if (!key["children"]){
            key["children"] = [];
            key["children"].push(treeArray[i]);
        }else{
          key["children"].push(treeArray[i]);
        }       
      } else {
        //如果沒有這個Key值,那就代表沒有父級,直接放在最外層
        r.push(treeArray[i]);
      }
    }
    return r
   }

現在我們已經實現了將沒有層級結構的數組轉化為帶有層級的數組,那麼問題來了,樹形圖還常常會需要帶搜索功能,有沒有辦法把帶層級結構的數組轉化為不帶層級結構的一個數組呢?因為如果不帶層級的話,進行搜索等操作時就非常方便,一個filter基本就可以搞定了。

   var jsonToArray=function (nodes) {
      var r=[];
      if (Array.isArray(nodes)) {
        for (var i=0, l=nodes.length; i<l; i++) {
          r.push(nodes[i]);
          if (Array.isArray(nodes[i]["children"])&&nodes[i]["children"].length>0)
           //將children遞歸的push到最外層的數組r裡面
            r = r.concat(jsonToArray(nodes[i]["children"]));
              delete nodes[i]["children"]
        }
      } 
      return r;
    }

這樣,不管後臺返回什麼格式給我們,我們都可以自由的互轉了,不管是帶層級的轉不帶層級的,還是把不帶層級的轉化為帶有層級的,都只需要調用一個函數就可以輕鬆解決。

不過這裡有一個隱患,就是由於對象的引用關係,操作後雖然返回了我們需要東西,但是會改變原來的數據。

為了不影響到原來的數據,我們需要複製一份數據,需要進行一次深拷貝。

為什麼是深拷貝而不是淺拷貝?因為淺拷貝只會複製最外面的一層,假入某一個key值裡面又是一個對象,那對複製後的對象的這個key的值進行操作通用會影響到原來的對象。淺拷貝的方法有很多,ES6的assign,jq第一個參數不為true的 $.extend(),數組的slice(0),還有很多很多。

對於標準的json格式的對象,可以用JSON.parse(JSON.stringify(obj))來實現。當然,本文寫的是遞歸,所以還是來手寫一個

function deepCopy(obj){
    var object;
    if(Object.prototype.toString.call(obj)=="[object Array]"){    
      object=[];
      for(var i=0;i<obj.length;i++){
        object.push(deepCopy(obj[i]))
      }   
      return object
    }

    if(Object.prototype.toString.call(obj)=="[object Object]"){   
      object={};
      for(var p in obj){
        object[p]=obj[p]
      }   
      return object
    }
  }

其實有點類似於淺拷貝,淺拷貝會複製一層,那麼我們判斷某個值是對象,通過遞歸再來一次(好比飲料中獎再來一瓶一樣,如果中獎了,就遞歸再來一瓶,又中獎就又遞歸再來一瓶,直到不再中獎),也就是說我們通過無盡的淺拷貝來達到複製一個完全的新的對象的效果。

這樣,對樹結構操作時,只需要傳入深拷貝後新對象,就不會影響原來的對象了;

jsonToArray(deepCopy(data));

亦或是

arrayToJson(deepCopy(data)):

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、練習一 1、需求效果分析: 2、代碼示例: 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; ...
  • 寫幾個簡單的載入中動畫吧。 像前面三種都是相當於幾個不同的點輪流來播放同一動畫:變大變小。css3裡面有一個用於尺度變換的方法:scale(x,y):定義 2D 縮放轉換,改變元素的寬度和高度。 第四種就是一個小球從上往下跌落,再彈回去,在上面的時候速度最小,下麵的時候速度最大。由於該小球只進行了上 ...
  • 使用Grunt構建項目涉及磁碟操作,構建效率較低,因此,基於流的Gulp應運而生。本節通過一個例子,介紹如何利用Gulp構建一個ECMAScript 6和Sass應用。 ...
  • redux原理 某公司有物流(actionType)、電商(actionType)、廣告(actionType)3塊業務,在公司財務系統(state)統一記錄著三塊業務分別賺取到的資金。某天,電商業務在公司電商平臺銷售了價值100w的商品(action),賺取到的資金通過發票(dispatch)的形 ...
  • 1、webstrom 11.0.3下載地址1:http://pan.baidu.com/s/1kVQjcwf 密碼:uggr 下載地址2:http://pan.baidu.com/s/1kVQjcwf,點擊DOWNLOAD即可下載 2、webstrom11激活 選擇“license server” ...
  • 網頁title旁邊的小圖片設置,圖片格式必須是.ico ...
  • 一、jQuery 中的常用函數 1) $.map(Array,fn); 對數組中的每個元素,都用fn進行處理,fn將處理後的結果返回,最後得到一個數組 2) $.each(Array,fn); 對數組中的每個元素,調用fn這個函數進行處理,但是,沒有返回值,比上例更常用 二、jQuery 對象和Do ...
  • 最近在前端開發中,遇到一個JavaScript 的問題。 用Chrome,Firefox,運行結果正常。但是在IE裡面確出現了"對象不支持find屬性或方法"的錯誤. 看了下官方文檔才發現他是不支持IE的。https://developer.mozilla.org/en-US/docs/Web/Ja ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...