สร้างอาร์เรย์ต้นไม้จากอาร์เรย์แบบแบนในจาวาสคริปต์


134

ฉันมีไฟล์ json ที่ซับซ้อนซึ่งฉันต้องจัดการด้วยจาวาสคริปต์เพื่อทำให้เป็นลำดับชั้นเพื่อที่จะสร้างต้นไม้ในภายหลัง ทุกรายการของ json มี: id: id เฉพาะ, parentId: id ของโหนดพาเรนต์ (ซึ่งเป็น 0 ถ้าโหนดเป็นรูทของทรี) ระดับ: ระดับของความลึกในทรี

ข้อมูล json "สั่งซื้อ" แล้ว ฉันหมายความว่ารายการจะมีโหนดแม่หรือโหนดบราเดอร์เหนือตัวเองและภายใต้โหนดลูกหรือโหนดพี่ชาย

อินพุต:

{
    "People": [
        {
            "id": "12",
            "parentId": "0",
            "text": "Man",
            "level": "1",
            "children": null
        },
        {
            "id": "6",
            "parentId": "12",
            "text": "Boy",
            "level": "2",
            "children": null
        },
                {
            "id": "7",
            "parentId": "12",
            "text": "Other",
            "level": "2",
            "children": null
        },
        {
            "id": "9",
            "parentId": "0",
            "text": "Woman",
            "level": "1",
            "children": null
        },
        {
            "id": "11",
            "parentId": "9",
            "text": "Girl",
            "level": "2",
            "children": null
        }
    ],
    "Animals": [
        {
            "id": "5",
            "parentId": "0",
            "text": "Dog",
            "level": "1",
            "children": null
        },
        {
            "id": "8",
            "parentId": "5",
            "text": "Puppy",
            "level": "2",
            "children": null
        },
        {
            "id": "10",
            "parentId": "13",
            "text": "Cat",
            "level": "1",
            "children": null
        },
        {
            "id": "14",
            "parentId": "13",
            "text": "Kitten",
            "level": "2",
            "children": null
        },
    ]
}

ผลลัพธ์ที่คาดหวัง:

{
    "People": [
        {
            "id": "12",
            "parentId": "0",
            "text": "Man",
            "level": "1",
            "children": [
                {
                    "id": "6",
                    "parentId": "12",
                    "text": "Boy",
                    "level": "2",
                    "children": null
                },
                {
                    "id": "7",
                    "parentId": "12",
                    "text": "Other",
                    "level": "2",
                    "children": null
                }   
            ]
        },
        {
            "id": "9",
            "parentId": "0",
            "text": "Woman",
            "level": "1",
            "children":
            {

                "id": "11",
                "parentId": "9",
                "text": "Girl",
                "level": "2",
                "children": null
            }
        }

    ],    

    "Animals": [
        {
            "id": "5",
            "parentId": "0",
            "text": "Dog",
            "level": "1",
            "children": 
                {
                    "id": "8",
                    "parentId": "5",
                    "text": "Puppy",
                    "level": "2",
                    "children": null
                }
        },
        {
            "id": "10",
            "parentId": "13",
            "text": "Cat",
            "level": "1",
            "children": 
            {
                "id": "14",
                "parentId": "13",
                "text": "Kitten",
                "level": "2",
                "children": null
            }
        }

    ]
}

2
มีหลายวิธีในการทำเช่นนั้นคุณได้ลองทำอะไรหรือยัง?
bfavaretto

ฉันคิดว่า a parentIdof 0mean ไม่มีรหัสหลักและควรเป็นชั้นบนสุด
Donnie D'Amato

1
โดยปกติงานประเภทนี้ต้องใช้วัตถุความรู้ในการทำงานที่กว้างขวาง คำถามที่ดี
Gangadhar JANNU

คำตอบ:


159

มีวิธีแก้ปัญหาที่มีประสิทธิภาพหากคุณใช้การค้นหาแผนที่ หากพ่อแม่มาก่อนลูกเสมอคุณสามารถรวมห่วงทั้งสองเข้าด้วยกันได้ รองรับหลายรูท มันทำให้เกิดข้อผิดพลาดบนกิ่งก้านห้อย แต่สามารถแก้ไขเพื่อละเว้นได้ ไม่ต้องใช้ไลบรารีของบุคคลที่สาม เท่าที่ฉันบอกได้วิธีแก้ปัญหาที่เร็วที่สุด

function list_to_tree(list) {
  var map = {}, node, roots = [], i;
  
  for (i = 0; i < list.length; i += 1) {
    map[list[i].id] = i; // initialize the map
    list[i].children = []; // initialize the children
  }
  
  for (i = 0; i < list.length; i += 1) {
    node = list[i];
    if (node.parentId !== "0") {
      // if you have dangling branches check that map[node.parentId] exists
      list[map[node.parentId]].children.push(node);
    } else {
      roots.push(node);
    }
  }
  return roots;
}

var entries = [{
    "id": "12",
    "parentId": "0",
    "text": "Man",
    "level": "1",
    "children": null
  },
  {
    "id": "6",
    "parentId": "12",
    "text": "Boy",
    "level": "2",
    "children": null
  },
  {
    "id": "7",
    "parentId": "12",
    "text": "Other",
    "level": "2",
    "children": null
  },
  {
    "id": "9",
    "parentId": "0",
    "text": "Woman",
    "level": "1",
    "children": null
  },
  {
    "id": "11",
    "parentId": "9",
    "text": "Girl",
    "level": "2",
    "children": null
  }
];

console.log(list_to_tree(entries));

หากคุณใช้ทฤษฎีความซับซ้อนคำตอบนี้คือΘ (n log (n)) โซลูชันตัวกรองแบบวนซ้ำคือΘ (n ^ 2) ซึ่งอาจเป็นปัญหาสำหรับชุดข้อมูลขนาดใหญ่


28
โปรดทราบว่าด้วยโซลูชันนี้โหนดของคุณต้องได้รับการสั่งซื้อโดยเฉพาะเพื่อให้แน่ใจว่าพ่อแม่ถูกผลักเข้าไปในแผนที่ก่อนมิฉะนั้นกระบวนการค้นหาจะผิดพลาด ... ดังนั้นคุณต้องเรียง em บนคุณสมบัติระดับหรือคุณต้องการ เพื่อผลักดันให้เข้าสู่แผนที่ก่อน และใช้แยกต่างหากสำหรับลูปสำหรับการค้นหา (ฉันชอบเรียงลำดับอย่างไรก็ตามเมื่อคุณไม่มีคุณสมบัติระดับลูปแยกอาจเป็นตัวเลือก)
แซนเดอร์

ฉันพบว่ามันน่าแปลกใจในตอนแรกที่มีข้อมูลเพิ่มเติมเช่นเส้นทางเช่น [1, 5, 6] ซึ่งอาร์เรย์เป็นบรรพบุรุษที่ตามมาไม่สามารถใช้งานได้อย่างมีประสิทธิภาพ แต่เมื่อมองไปที่รหัสมันทำให้รู้สึกได้เพราะฉันเชื่อว่ามันเป็น O (n)
Ced

1
แม้จะมีคำตอบที่ดี แต่ก็มีความซับซ้อน ใช้คำตอบของฉันด้วยรหัสสองบรรทัด: ลิงค์
Iman Bahrampour

ช่วยอธิบายได้ไหมว่าทำไมวิธีนี้จึงเป็นΘ (n log (n)) ดูเหมือนว่าจะใช้เวลา O (n)
61

@amrendersingh ภายใน for-loop คือ hash-lookup mapซึ่ง (ในทางทฤษฎี) คือ O (log n)
Halcyon

72

ดังที่ @Sander กล่าวไว้คำตอบของ @ Halcyonถือว่าอาร์เรย์ที่เรียงไว้ล่วงหน้าสิ่งต่อไปนี้ไม่ได้ (อย่างไรก็ตามสมมติว่าคุณโหลด underscore.js - แม้ว่าจะสามารถเขียนด้วย vanilla javascript):

รหัส

// Example usage
var arr = [
    {'id':1 ,'parentid' : 0},
    {'id':2 ,'parentid' : 1},
    {'id':3 ,'parentid' : 1},
    {'id':4 ,'parentid' : 2},
    {'id':5 ,'parentid' : 0},
    {'id':6 ,'parentid' : 0},
    {'id':7 ,'parentid' : 4}
];

unflatten = function( array, parent, tree ){
    tree = typeof tree !== 'undefined' ? tree : [];
    parent = typeof parent !== 'undefined' ? parent : { id: 0 };
        
    var children = _.filter( array, function(child){ return child.parentid == parent.id; });
    
    if( !_.isEmpty( children )  ){
        if( parent.id == 0 ){
           tree = children;   
        }else{
           parent['children'] = children
        }
        _.each( children, function( child ){ unflatten( array, child ) } );                    
    }
    
    return tree;
}

tree = unflatten( arr );
document.body.innerHTML = "<pre>" + (JSON.stringify(tree, null, " "))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

ความต้องการ

จะถือว่าคุณสมบัติ 'id' และ 'parentid' ระบุ ID และ ID พาเรนต์ตามลำดับ ต้องมีองค์ประกอบที่มี ID หลัก 0 มิฉะนั้นคุณจะได้รับอาร์เรย์ว่างเปล่ากลับมา องค์ประกอบที่กำพร้าและลูกหลานของพวกเขาจะ 'สูญหาย'

http://jsfiddle.net/LkkwH/1/


4
คุณสามารถเพิ่มelse { parent['children'] = []; }หลัง if-clause แรกเพื่อให้แน่ใจว่าทุกโหนดมีแอตทริบิวต์children(จะว่างเปล่าถ้าโหนดนั้นเป็นโหนดลีฟ)
Christopher

2
ข้อมูลโค้ดของคุณทำงานได้อย่างสมบูรณ์ขอบคุณ !! สิ่งเดียวคือ: treeจะไม่ถูกส่งผ่านเป็นอาร์กิวเมนต์เมื่อเรียกใช้ฟังก์ชันแบบวนซ้ำดังนั้นฉันคิดว่าบรรทัดtree = typeof tree !== 'undefined' ? tree : [];นี้สามารถแทนที่ได้let tree = [];
Oscar Calderon

สามารถแก้ไขเพื่ออนุญาตnullparent_ids แทน 0 ได้หรือไม่ แก้ไข:ไม่เป็นไรผมได้รับมันทำงานโดยการเปลี่ยนไปid: 0 id: null
dlinx90

โปรดทราบว่าคำตอบข้างต้นใช้สองลูปดังนั้นจึงควรปรับปรุง เนื่องจากฉันไม่พบโมดูล npm ที่ใช้โซลูชัน O (n) ฉันจึงสร้างสิ่งต่อไปนี้ (ทดสอบหน่วย, การครอบคลุมรหัส 100%, ขนาด 0.5 กิโลไบต์เท่านั้นและรวมการพิมพ์ด้วย) บางทีมันอาจช่วยใครบางคนได้: npmjs.com/package/performant-array-to-tree
Philip Stanislaus

4
สำหรับใครที่สนใจโค้ดสามารถแปลงเป็น vanilla js ได้ง่ายๆ
xec

48

(โบนัส 1: โหนดอาจหรืออาจไม่มีการสั่งซื้อ)

(BONUS2: ไม่จำเป็นต้องใช้ไลบรารีของบุคคลที่ 3RD, PLAIN JS)

(โบนัส 3: ผู้ใช้ "Elias Rabl" บอกว่านี่เป็นวิธีแก้ปัญหาที่เร็วที่สุดดูคำตอบของเขาด้านล่าง)

นี่คือ:

const createDataTree = dataset => {
    let hashTable = Object.create(null)
    dataset.forEach( aData => hashTable[aData.ID] = { ...aData, childNodes : [] } )
    let dataTree = []
    dataset.forEach( aData => {
      if( aData.parentID ) hashTable[aData.parentID].childNodes.push(hashTable[aData.ID])
      else dataTree.push(hashTable[aData.ID])
    } )
    return dataTree
}

นี่คือการทดสอบซึ่งอาจช่วยให้คุณเข้าใจวิธีการแก้ปัญหา:

it('creates a correct shape of dataTree', () => {

    let dataSet = [
        {
            "ID": 1,
            "Phone": "(403) 125-2552",
            "City": "Coevorden",
            "Name": "Grady"
        },
        {
            "ID": 2,
            "parentID": 1,
            "Phone": "(979) 486-1932",
            "City": "Chełm",
            "Name": "Scarlet"
        }
    ]

    let expectedDataTree = [ 
    {
            "ID": 1,
            "Phone": "(403) 125-2552",
            "City": "Coevorden",
            "Name": "Grady",
            childNodes : [
                {
                    "ID": 2,
                    "parentID": 1,
                    "Phone": "(979) 486-1932",
                    "City": "Chełm",
                    "Name": "Scarlet",
                    childNodes : []
                }
            ]
    } 
    ]

  expect( createDataTree(dataSet) ).toEqual(expectedDataTree)
});

3
จะไม่แม่นยำกว่านี้หรือถ้าเราเพิ่มchildNodesเฉพาะเมื่อจำเป็น? โดยการลบออกจากครั้งแรกforEachและย้ายเข้าไปในที่สอง?
arpl

@arpl เห็นด้วย หนึ่งสามารถเปลี่ยนแปลงได้อย่างง่ายดายหากจำเป็น หรือถ้าคุณคิดว่าควรเป็นค่าเริ่มต้นฉันสามารถเปลี่ยนได้
FurkanO

@FurkanO เป็นทางออกที่ดีจริงๆอย่างไรก็ตามจะเป็นไปได้หรือไม่ที่จะไปได้ทุกที่ใกล้กับประสิทธิภาพนี้ด้วยการเขียนโปรแกรมเชิงฟังก์ชัน (ไม่มีการกลายพันธุ์)
Dac0d3r

34

มีปัญหาเดียวกัน แต่ฉันไม่สามารถจะมั่นใจได้ว่าข้อมูลที่ถูกจัดเรียงหรือไม่ ฉันไม่สามารถใช้ไลบรารีของบุคคลที่สามได้ดังนั้นนี่จึงเป็นเพียงวานิลลา Js; ข้อมูลอินพุตสามารถนำมาจากตัวอย่างของ @ Stephen;

 var arr = [
        {'id':1 ,'parentid' : 0},
        {'id':4 ,'parentid' : 2},
        {'id':3 ,'parentid' : 1},
        {'id':5 ,'parentid' : 0},
        {'id':6 ,'parentid' : 0},
        {'id':2 ,'parentid' : 1},
        {'id':7 ,'parentid' : 4},
        {'id':8 ,'parentid' : 1}
      ];
    function unflatten(arr) {
      var tree = [],
          mappedArr = {},
          arrElem,
          mappedElem;

      // First map the nodes of the array to an object -> create a hash table.
      for(var i = 0, len = arr.length; i < len; i++) {
        arrElem = arr[i];
        mappedArr[arrElem.id] = arrElem;
        mappedArr[arrElem.id]['children'] = [];
      }


      for (var id in mappedArr) {
        if (mappedArr.hasOwnProperty(id)) {
          mappedElem = mappedArr[id];
          // If the element is not at the root level, add it to its parent array of children.
          if (mappedElem.parentid) {
            mappedArr[mappedElem['parentid']]['children'].push(mappedElem);
          }
          // If the element is at the root level, add it to first level elements array.
          else {
            tree.push(mappedElem);
          }
        }
      }
      return tree;
    }

var tree = unflatten(arr);
document.body.innerHTML = "<pre>" + (JSON.stringify(tree, null, " "))

JS Fiddle

Flat Array กับ Tree


ในบางกรณีmappedArr[mappedElem['parentid']]['children']ล้มเหลวเนื่องจากไม่สามารถเข้าถึงchildrenundefined ได้
Al-Mothafar

ฉันจะเริ่มต้นที่รหัสผู้ปกครอง: 1 ได้อย่างไร
vinni

31

ใช้แนวทาง ES6 นี้ ใช้งานได้เหมือนมีเสน่ห์

// Data Set
// One top level comment 
const comments = [{
    id: 1,
    parent_id: null
}, {
    id: 2,
    parent_id: 1
}, {
    id: 3,
    parent_id: 1
}, {
    id: 4,
    parent_id: 2
}, {
    id: 5,
    parent_id: 4
}];

const nest = (items, id = null, link = 'parent_id') =>
  items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id) }));

console.log(
  nest(comments)
)


3
คำตอบที่สั้นและดีที่สุดที่ฉันคิด
java-man-script

2
sloooow เมื่อเทียบกับคำตอบของ FurkanO
Geza Turi

16

ฟังก์ชั่นที่เรียบง่ายมากขึ้นlist-to-tree-lite

npm install list-to-tree-lite

listToTree(list)

แหล่งที่มา:

function listToTree(data, options) {
    options = options || {};
    var ID_KEY = options.idKey || 'id';
    var PARENT_KEY = options.parentKey || 'parent';
    var CHILDREN_KEY = options.childrenKey || 'children';

    var tree = [],
        childrenOf = {};
    var item, id, parentId;

    for (var i = 0, length = data.length; i < length; i++) {
        item = data[i];
        id = item[ID_KEY];
        parentId = item[PARENT_KEY] || 0;
        // every item may have children
        childrenOf[id] = childrenOf[id] || [];
        // init its children
        item[CHILDREN_KEY] = childrenOf[id];
        if (parentId != 0) {
            // init its parent's children object
            childrenOf[parentId] = childrenOf[parentId] || [];
            // push it into its parent's children object
            childrenOf[parentId].push(item);
        } else {
            tree.push(item);
        }
    };

    return tree;
}

jsfiddle


10

คุณสามารถจัดการคำถามนี้ได้ด้วยการเข้ารหัสเพียงสองบรรทัด:

_(flatArray).forEach(f=>
           {f.nodes=_(flatArray).filter(g=>g.parentId==f.id).value();});

var resultArray=_(flatArray).filter(f=>f.parentId==null).value();

ทดสอบออนไลน์ (ดูคอนโซลเบราว์เซอร์สำหรับโครงสร้างที่สร้างขึ้น)

ที่ต้องการ:

1- ติดตั้ง lodash 4 (ไลบรารี Javascript สำหรับจัดการอ็อบเจ็กต์และคอลเลคชันด้วยวิธีการดำเนินการ => เช่น Linq ใน c #) Lodash

2- flatArray ดังด้านล่าง:

    var flatArray=
    [{
      id:1,parentId:null,text:"parent1",nodes:[]
    }
   ,{
      id:2,parentId:null,text:"parent2",nodes:[]
    }
    ,
    {
      id:3,parentId:1,text:"childId3Parent1",nodes:[]
    }
    ,
    {
      id:4,parentId:1,text:"childId4Parent1",nodes:[]
    }
    ,
    {
      id:5,parentId:2,text:"childId5Parent2",nodes:[]
    }
    ,
    {
      id:6,parentId:2,text:"childId6Parent2",nodes:[]
    }
    ,
    {
      id:7,parentId:3,text:"childId7Parent3",nodes:[]
    }
    ,
    {
      id:8,parentId:5,text:"childId8Parent5",nodes:[]
    }];

ขอบคุณนาย Bakhshabadi

โชคดี


8

อาจเป็นแพ็คเกจที่มีประโยชน์ ติดตั้งแบบรายการต่อต้นไม้ :

bower install list-to-tree --save

หรือ

npm install list-to-tree --save

ตัวอย่างเช่นมีรายการ:

var list = [
  {
    id: 1,
    parent: 0
  }, {
    id: 2,
    parent: 1
  }, {
    id: 3,
    parent: 1
  }, {
    id: 4,
    parent: 2
  }, {
    id: 5,
    parent: 2
  }, {
    id: 6,
    parent: 0
  }, {
    id: 7,
    parent: 0
  }, {
    id: 8,
    parent: 7
  }, {
    id: 9,
    parent: 8
  }, {
    id: 10,
    parent: 0
  }
];

ใช้ package list-to-tree:

var ltt = new LTT(list, {
  key_id: 'id',
  key_parent: 'parent'
});
var tree = ltt.GetTree();

ผลลัพธ์:

[{
  "id": 1,
  "parent": 0,
  "child": [
    {
      "id": 2,
      "parent": 1,
      "child": [
        {
          "id": 4,
          "parent": 2
        }, {
          "id": 5, "parent": 2
        }
      ]
    },
    {
      "id": 3,
      "parent": 1
    }
  ]
}, {
  "id": 6,
  "parent": 0
}, {
  "id": 7,
  "parent": 0,
  "child": [
    {
      "id": 8,
      "parent": 7,
      "child": [
        {
          "id": 9,
          "parent": 8
        }
      ]
    }
  ]
}, {
  "id": 10,
  "parent": 0
}];

1
โปรดทราบว่าคำตอบแบบลิงก์เท่านั้นไม่ได้รับการสนับสนุนคำตอบ SO ควรเป็นจุดสิ้นสุดของการค้นหาวิธีแก้ปัญหา (เทียบกับการหยุดการอ้างอิงอื่น ๆ ซึ่งมักจะค้างเมื่อเวลาผ่านไป) โปรดพิจารณาเพิ่มเรื่องย่อแบบสแตนด์อโลนที่นี่โดยเก็บลิงค์ไว้เป็นข้อมูลอ้างอิง
kleopatra

ฉันไม่เข้าใจว่าทำไม -1 ฉันคิดว่ามันเป็นทางออกที่ดี แต่น่าเสียดายที่ฉันไม่พบแพ็คเกจใน gitHub หรือในที่เก็บสาธารณะอื่น
oriaj

ขอบคุณสำหรับความสนใจของคุณแพคเกจ ฉันวางแผนที่จะขยายในภายหลัง นี่คือลิงค์ไปยังที่เก็บgithub.com/DenQ/list-to-tree
DenQ

@oriaj ดีใจที่โครงการได้รับประโยชน์ แผนของความคิดบางอย่าง
DenQ

ใช้งานได้ดีขอบคุณ @DenQ หวังว่ามันจะครอบคลุมการทดสอบมากขึ้น!
IliasT

3

ฉันได้เขียนสคริปต์ทดสอบเพื่อประเมินประสิทธิภาพของโซลูชันทั่วไปส่วนใหญ่สองรายการ (หมายความว่าข้อมูลไม่จำเป็นต้องเรียงลำดับล่วงหน้าและรหัสไม่ได้ขึ้นอยู่กับไลบรารีของบุคคลที่สาม) ซึ่งเสนอโดยผู้ใช้ shekhardtu ( ดูคำตอบ ) และ FurkanO ( ดูคำตอบ )

http://playcode.io/316025?tabs=console&script.js&output

ทางออกของ FurkanO ดูเหมือนจะเร็วที่สุด

/*
** performance test for /programming/18017869/build-tree-array-from-flat-array-in-javascript
*/

// Data Set (e.g. nested comments)
var comments = [{
    id: 1,
    parent_id: null
}, {
    id: 2,
    parent_id: 1
}, {
    id: 3,
    parent_id: 4
}, {
    id: 4,
    parent_id: null
}, {
    id: 5,
    parent_id: 4
}];

// add some random entries
let maxParentId = 10000;
for (let i=6; i<=maxParentId; i++)
{
  let randVal = Math.floor((Math.random() * maxParentId) + 1);
  comments.push({
    id: i,
    parent_id: (randVal % 200 === 0 ? null : randVal)
  });
}

// solution from user "shekhardtu" (https://stackoverflow.com/a/55241491/5135171)
const nest = (items, id = null, link = 'parent_id') =>
  items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id) }));
;

// solution from user "FurkanO" (https://stackoverflow.com/a/40732240/5135171)
const createDataTree = dataset => {
    let hashTable = Object.create(null)
    dataset.forEach( aData => hashTable[aData.id] = { ...aData, children : [] } )
    let dataTree = []
    dataset.forEach( aData => {
      if( aData.parent_id ) hashTable[aData.parent_id].children.push(hashTable[aData.id])
      else dataTree.push(hashTable[aData.id])
    } )
    return dataTree
};


/*
** lets evaluate the timing for both methods
*/
let t0 = performance.now();
let createDataTreeResult = createDataTree(comments);
let t1 = performance.now();
console.log("Call to createDataTree took " + Math.floor(t1 - t0) + " milliseconds.");

t0 = performance.now();
let nestResult = nest(comments);
t1 = performance.now();
console.log("Call to nest took " + Math.floor(t1 - t0) + " milliseconds.");




//console.log(nestResult);
//console.log(createDataTreeResult);

// bad, but simple way of comparing object equality
console.log(JSON.stringify(nestResult)===JSON.stringify(createDataTreeResult));


2

นี่คือข้อเสนอสำหรับรายการที่ไม่ได้เรียงลำดับ ฟังก์ชันนี้ใช้งานได้กับลูปเดียวและตารางแฮชและรวบรวมรายการทั้งหมดด้วยidไฟล์. หากพบโหนดรูทวัตถุจะถูกเพิ่มลงในอาร์เรย์ผลลัพธ์

function getTree(data, root) {
    var o = {};
    data.forEach(function (a) {
        if (o[a.id] && o[a.id].children) {
            a.children = o[a.id].children;
        }
        o[a.id] = a;
        o[a.parentId] = o[a.parentId] || {};
        o[a.parentId].children = o[a.parentId].children || [];
        o[a.parentId].children.push(a);
    });
    return o[root].children;
}

var data = { People: [{ id: "12", parentId: "0", text: "Man", level: "1", children: null }, { id: "6", parentId: "12", text: "Boy", level: "2", children: null }, { id: "7", parentId: "12", text: "Other", level: "2", children: null }, { id: "9", parentId: "0", text: "Woman", level: "1", children: null }, { id: "11", parentId: "9", text: "Girl", level: "2", children: null }], Animals: [{ id: "5", parentId: "0", text: "Dog", level: "1", children: null }, { id: "8", parentId: "5", text: "Puppy", level: "2", children: null }, { id: "10", parentId: "13", text: "Cat", level: "1", children: null }, { id: "14", parentId: "13", text: "Kitten", level: "2", children: null }] },
    tree = Object.keys(data).reduce(function (r, k) {
        r[k] = getTree(data[k], '0');
        return r;
    }, {});

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }



1

ฉันชอบโซลูชัน JavaScript ที่บริสุทธิ์ของ @ WilliamLeung แต่บางครั้งคุณต้องทำการเปลี่ยนแปลงในอาร์เรย์ที่มีอยู่เพื่อให้อ้างอิงถึงวัตถุ

function listToTree(data, options) {
  options = options || {};
  var ID_KEY = options.idKey || 'id';
  var PARENT_KEY = options.parentKey || 'parent';
  var CHILDREN_KEY = options.childrenKey || 'children';

  var item, id, parentId;
  var map = {};
    for(var i = 0; i < data.length; i++ ) { // make cache
    if(data[i][ID_KEY]){
      map[data[i][ID_KEY]] = data[i];
      data[i][CHILDREN_KEY] = [];
    }
  }
  for (var i = 0; i < data.length; i++) {
    if(data[i][PARENT_KEY]) { // is a child
      if(map[data[i][PARENT_KEY]]) // for dirty data
      {
        map[data[i][PARENT_KEY]][CHILDREN_KEY].push(data[i]); // add child to parent
        data.splice( i, 1 ); // remove from root
        i--; // iterator correction
      } else {
        data[i][PARENT_KEY] = 0; // clean dirty data
      }
    }
  };
  return data;
}

Exapmle: https://jsfiddle.net/kqw1qsf0/17/


1

var data = [{"country":"india","gender":"male","type":"lower","class":"X"},
			{"country":"china","gender":"female","type":"upper"},
			{"country":"india","gender":"female","type":"lower"},
			{"country":"india","gender":"female","type":"upper"}];
var seq = ["country","type","gender","class"];
var treeData = createHieArr(data,seq);
console.log(treeData)
function createHieArr(data,seq){
	var hieObj = createHieobj(data,seq,0),
		hieArr = convertToHieArr(hieObj,"Top Level");
		return [{"name": "Top Level", "parent": "null",
				     "children" : hieArr}]
	function convertToHieArr(eachObj,parent){
		var arr = [];
		for(var i in eachObj){
			arr.push({"name":i,"parent":parent,"children":convertToHieArr(eachObj[i],i)})
		}
		return arr;
	}
	function createHieobj(data,seq,ind){
		var s = seq[ind];
		if(s == undefined){
			return [];
		}
		var childObj = {};
		for(var ele of data){
			if(ele[s] != undefined){
				if(childObj[ele[s]] == undefined){
					childObj[ele[s]] = [];
				}
				childObj[ele[s]].push(ele);
			}
		}
		ind = ind+1;
		for(var ch in childObj){
			childObj[ch] = createHieobj(childObj[ch],seq,ind)
		}
		return childObj;
	}
}


ฉันสร้างฟังก์ชันนี้เพื่อแปลงข้อมูลจากอาร์เรย์ของวัตถุเป็นโครงสร้างต้นไม้ซึ่งจำเป็นสำหรับแผนภูมิโต้ตอบแบบต้นไม้ d3 ด้วยรหัสเพียง 40 บรรทัดฉันสามารถรับเอาต์พุตได้ ฉันเขียนฟังก์ชันนี้ด้วยวิธีที่มีประสิทธิภาพโดยใช้ฟังก์ชันเรียกซ้ำใน js ลองแจ้งให้เราทราบความคิดเห็นของคุณ ขอบคุณ!!!!
karthik reddy

ขอบคุณสำหรับ anwser .. มันทำงานได้อย่างสมบูรณ์แบบสำหรับโครงสร้างโครงสร้างต้นไม้ d3 ของฉันตอนนี้ฉันมีข้อกำหนดว่าฉันต้องเปลี่ยนสีโหนดตามค่าของโหนด .. ดังนั้นฉันต้องส่งค่าแฟล็กใน JSON . ฉันจะทำเช่นนั้นได้อย่างไร .. {"name": "ระดับบนสุด", "flag": 1, "parent": "null", "children": [{"name": "india", "flag": 0 , "parent": "Top Level", "children": [
Puneeth Kumar

1

ฉันมีปัญหาคล้ายกันเมื่อสองสามวันก่อนเมื่อต้องแสดงแผนผังโฟลเดอร์จากอาร์เรย์แบบแบน ฉันไม่เห็นวิธีแก้ปัญหาใด ๆ ใน TypeScript ที่นี่ดังนั้นฉันหวังว่ามันจะเป็นประโยชน์

ในกรณีของฉันพาเรนต์หลักมีเพียงอันเดียวอาร์เรย์ rawData ก็ไม่จำเป็นต้องเรียงลำดับ การแก้ปัญหาขึ้นอยู่กับการเตรียมวัตถุชั่วคราวเช่น {parentId: [child1, child2, ...] }

ตัวอย่างข้อมูลดิบ

const flatData: any[] = Folder.ofCollection([
  {id: '1', title: 'some title' },
  {id: '2', title: 'some title', parentId: 1 },
  {id: '3', title: 'some title', parentId: 7 },
  {id: '4', title: 'some title', parentId: 1 },
  {id: '5', title: 'some title', parentId: 2 },
  {id: '6', title: 'some title', parentId: 5 },
  {id: '7', title: 'some title', parentId: 5 },

]);

def ของโฟลเดอร์

export default class Folder {
    public static of(data: any): Folder {
        return new Folder(data);
    }

    public static ofCollection(objects: any[] = []): Folder[] {
        return objects.map((obj) => new Folder(obj));
    }

    public id: string;
    public parentId: string | null;
    public title: string;
    public children: Folder[];

    constructor(data: any = {}) {
        this.id = data.id;
        this.parentId = data.parentId || null;
        this.title = data.title;
        this.children = data.children || [];
    }
}

SOLUTION : ฟังก์ชันที่ส่งคืนโครงสร้างต้นไม้สำหรับอาร์กิวเมนต์แบบแบน

    public getTree(flatData: any[]): Folder[] {
        const addChildren = (item: Folder) => {
            item.children = tempChild[item.id] || [];
            if (item.children.length) {
                item.children.forEach((child: Folder) => {
                    addChildren(child);
                });
            }
        };

        const tempChild: any = {};
        flatData.forEach((item: Folder) => {
            const parentId = item.parentId || 0;
            Array.isArray(tempChild[parentId]) ? tempChild[parentId].push(item) : (tempChild[parentId] = [item]);
        });

        const tree: Folder[] = tempChild[0];
        tree.forEach((base: Folder) => {
            addChildren(base);
        });
        return tree;
    }

0

นี่คือฟังก์ชั่นตัวช่วยง่ายๆที่ฉันสร้างขึ้นโดยจำลองตามคำตอบข้างต้นซึ่งปรับให้เหมาะกับสภาพแวดล้อม Babel:

import { isEmpty } from 'lodash'

export default function unflattenEntities(entities, parent = {id: null}, tree = []) {

  let children = entities.filter( entity => entity.parent_id == parent.id)

  if (!isEmpty( children )) {
    if ( parent.id == null ) {
      tree = children
    } else {
      parent['children'] = children
    }
    children.map( child => unflattenEntities( entities, child ) )
  }

  return tree

}

0

นี่คือเวอร์ชันแก้ไขของ Steven Harris ซึ่งเป็น ES5 ธรรมดาและส่งคืนอ็อบเจ็กต์ที่คีย์บน id แทนที่จะส่งคืนอาร์เรย์ของโหนดทั้งในระดับบนสุดและสำหรับกลุ่มย่อย

unflattenToObject = function(array, parent) {
  var tree = {};
  parent = typeof parent !== 'undefined' ? parent : {id: 0};

  var childrenArray = array.filter(function(child) {
    return child.parentid == parent.id;
  });

  if (childrenArray.length > 0) {
    var childrenObject = {};
    // Transform children into a hash/object keyed on token
    childrenArray.forEach(function(child) {
      childrenObject[child.id] = child;
    });
    if (parent.id == 0) {
      tree = childrenObject;
    } else {
      parent['children'] = childrenObject;
    }
    childrenArray.forEach(function(child) {
      unflattenToObject(array, child);
    })
  }

  return tree;
};

var arr = [
    {'id':1 ,'parentid': 0},
    {'id':2 ,'parentid': 1},
    {'id':3 ,'parentid': 1},
    {'id':4 ,'parentid': 2},
    {'id':5 ,'parentid': 0},
    {'id':6 ,'parentid': 0},
    {'id':7 ,'parentid': 4}
];
tree = unflattenToObject(arr);

0

นี่เป็นเวอร์ชันแก้ไขข้างต้นที่ทำงานร่วมกับรายการรูทหลายรายการฉันใช้ GUID สำหรับรหัสและพาเรนต์ไอเท็มของฉันดังนั้นใน UI ที่สร้างไอเท็มรูทรหัสยากให้กับบางสิ่งเช่น 0000000-00000-00000-TREE-ROOT-ITEM

var tree = unflatten (บันทึก "TREE-ROOT-ITEM");

function unflatten(records, rootCategoryId, parent, tree){
    if(!_.isArray(tree)){
        tree = [];
        _.each(records, function(rec){
            if(rec.parentId.indexOf(rootCategoryId)>=0){        // change this line to compare a root id
            //if(rec.parentId == 0 || rec.parentId == null){    // example for 0 or null
                var tmp = angular.copy(rec);
                tmp.children = _.filter(records, function(r){
                    return r.parentId == tmp.id;
                });
                tree.push(tmp);
                //console.log(tree);
                _.each(tmp.children, function(child){
                    return unflatten(records, rootCategoryId, child, tree);
                });
            }
        });
    }
    else{
        if(parent){
            parent.children = _.filter(records, function(r){
                return r.parentId == parent.id;
            });
            _.each(parent.children, function(child){
                return unflatten(records, rootCategoryId, child, tree);
            });
        }
    }
    return tree;
}

0

คัดลอกจากอินเทอร์เน็ต http://jsfiddle.net/stywell/k9x2a3g6/

    function list2tree(data, opt) {
        opt = opt || {};
        var KEY_ID = opt.key_id || 'ID';
        var KEY_PARENT = opt.key_parent || 'FatherID';
        var KEY_CHILD = opt.key_child || 'children';
        var EMPTY_CHILDREN = opt.empty_children;
        var ROOT_ID = opt.root_id || 0;
        var MAP = opt.map || {};
        function getNode(id) {
            var node = []
            for (var i = 0; i < data.length; i++) {
                if (data[i][KEY_PARENT] == id) {
                    for (var k in MAP) {
                        data[i][k] = data[i][MAP[k]];
                    }
                    if (getNode(data[i][KEY_ID]) !== undefined) {
                        data[i][KEY_CHILD] = getNode(data[i][KEY_ID]);
                    } else {
                        if (EMPTY_CHILDREN === null) {
                            data[i][KEY_CHILD] = null;
                        } else if (JSON.stringify(EMPTY_CHILDREN) === '[]') {
                            data[i][KEY_CHILD] = [];
                        }
                    }
                    node.push(data[i]);
                }
            }
            if (node.length == 0) {
                return;
            } else {
                return node;
            }
        }
        return getNode(ROOT_ID)
    }

    var opt = {
        "key_id": "ID",              //节点的ID
        "key_parent": "FatherID",    //节点的父级ID
        "key_child": "children",     //子节点的名称
        "empty_children": [],        //子节点为空时,填充的值  //这个参数为空时,没有子元素的元素不带key_child属性;还可以为null或者[],同理
        "root_id": 0,                //根节点的父级ID
        "map": {                     //在节点内映射一些值  //对象的键是节点的新属性; 对象的值是节点的老属性,会赋值给新属性
            "value": "ID",
            "label": "TypeName",
        }
    };

0

คุณสามารถใช้แพคเกจอาร์เรย์ต้นไม้เพื่อ NPM https://github.com/alferov/array-to-tree เป็นการแปลงอาร์เรย์ของโหนดธรรมดา (พร้อมตัวชี้ไปยังโหนดหลัก) เป็นโครงสร้างข้อมูลที่ซ้อนกัน

แก้ปัญหาเกี่ยวกับการแปลงการดึงข้อมูลจากชุดฐานข้อมูลไปเป็นโครงสร้างข้อมูลที่ซ้อนกัน (เช่นแผนผังการนำทาง)

การใช้งาน:

var arrayToTree = require('array-to-tree');

var dataOne = [
  {
    id: 1,
    name: 'Portfolio',
    parent_id: undefined
  },
  {
    id: 2,
    name: 'Web Development',
    parent_id: 1
  },
  {
    id: 3,
    name: 'Recent Works',
    parent_id: 2
  },
  {
    id: 4,
    name: 'About Me',
    parent_id: undefined
  }
];

arrayToTree(dataOne);

/*
 * Output:
 *
 * Portfolio
 *   Web Development
 *     Recent Works
 * About Me
 */

0

นี่คือสิ่งที่ฉันใช้ในโครงการปฏิกิริยา

// ListToTree.js
import _filter from 'lodash/filter';
import _map from 'lodash/map';

export default (arr, parentIdKey) => _map(_filter(arr, ar => !ar[parentIdKey]), ar => ({
  ...ar,
  children: _filter(arr, { [parentIdKey]: ar.id }),
}));

การใช้งาน:

// somewhere.js
import ListToTree from '../Transforms/ListToTree';

const arr = [
   {
      "id":"Bci6XhCLZKPXZMUztm1R",
      "name":"Sith"
   },
   {
      "id":"C3D71CMmASiR6FfDPlEy",
      "name":"Luke",
      "parentCategoryId":"ltatOlEkHdVPf49ACCMc"
   },
   {
      "id":"aS8Ag1BQqxkO6iWBFnsf",
      "name":"Obi Wan",
      "parentCategoryId":"ltatOlEkHdVPf49ACCMc"
   },
   {
      "id":"ltatOlEkHdVPf49ACCMc",
      "name":"Jedi"
   },
   {
      "id":"pw3CNdNhnbuxhPar6nOP",
      "name":"Palpatine",
      "parentCategoryId":"Bci6XhCLZKPXZMUztm1R"
   }
];
const response = ListToTree(arr, 'parentCategoryId');

เอาท์พุท:

[
   {
      "id":"Bci6XhCLZKPXZMUztm1R",
      "name":"Sith",
      "children":[
         {
            "id":"pw3CNdNhnbuxhPar6nOP",
            "name":"Palpatine",
            "parentCategoryId":"Bci6XhCLZKPXZMUztm1R"
         }
      ]
   },
   {
      "id":"ltatOlEkHdVPf49ACCMc",
      "name":"Jedi",
      "children":[
         {
            "id":"C3D71CMmASiR6FfDPlEy",
            "name":"Luke",
            "parentCategoryId":"ltatOlEkHdVPf49ACCMc"
         },
         {
            "id":"aS8Ag1BQqxkO6iWBFnsf",
            "name":"Obi Wan",
            "parentCategoryId":"ltatOlEkHdVPf49ACCMc"
         }
      ]
   }
]```


0

วิธีแก้ปัญหา typescript ของฉันอาจช่วยคุณได้:

type ITreeItem<T> = T & {
    children: ITreeItem<T>[],
};

type IItemKey = string | number;

function createTree<T>(
    flatList: T[],
    idKey: IItemKey,
    parentKey: IItemKey,
): ITreeItem<T>[] {
    const tree: ITreeItem<T>[] = [];

    // hash table.
    const mappedArr = {};
    flatList.forEach(el => {
        const elId: IItemKey = el[idKey];

        mappedArr[elId] = el;
        mappedArr[elId].children = [];
    });

    // also you can use Object.values(mappedArr).forEach(...
    // but if you have element which was nested more than one time
    // you should iterate flatList again:
    flatList.forEach((elem: ITreeItem<T>) => {
        const mappedElem = mappedArr[elem[idKey]];

        if (elem[parentKey]) {
            mappedArr[elem[parentKey]].children.push(elem);
        } else {
            tree.push(mappedElem);
        }
    });

    return tree;
}

ตัวอย่างการใช้งาน:

createTree(yourListData, 'id', 'parentId');

0

ฉันเขียนเวอร์ชัน ES6 ตามคำตอบของ @Halcyon

const array = [
  {
    id: '12',
    parentId: '0',
    text: 'one-1'
  },
  {
    id: '6',
    parentId: '12',
    text: 'one-1-6'
  },
  {
    id: '7',
    parentId: '12',
    text: 'one-1-7'
  },

  {
    id: '9',
    parentId: '0',
    text: 'one-2'
  },
  {
    id: '11',
    parentId: '9',
    text: 'one-2-11'
  }
];

// Prevent changes to the original data
const arrayCopy = array.map(item => ({ ...item }));

const listToTree = list => {
  const map = {};
  const roots = [];

  list.forEach((v, i) => {
    map[v.id] = i;
    list[i].children = [];
  });

  list.forEach(v => (v.parentId !== '0' ? list[map[v.parentId]].children.push(v) : roots.push(v)));

  return roots;
};

console.log(listToTree(arrayCopy));

หลักการของอัลกอริทึมนี้คือการใช้ "แผนที่" เพื่อสร้างความสัมพันธ์ของดัชนี การค้นหา "รายการ" ในรายการโดยใช้ "parentId" เป็นเรื่องง่ายและเพิ่ม "ลูก" ให้กับ "รายการ" แต่ละรายการเนื่องจาก "รายการ" เป็นความสัมพันธ์อ้างอิงดังนั้น "ราก" จะสร้างความสัมพันธ์กับต้นไม้ทั้งหมด


0

ตอบคำถามที่คล้ายกัน:

https://stackoverflow.com/a/61575152/7388356

UPDATE

คุณสามารถใช้Mapวัตถุที่นำมาใช้ใน ES6 โดยทั่วไปแทนที่จะค้นหาผู้ปกครองโดยการทำซ้ำในอาร์เรย์อีกครั้งคุณจะได้รับรายการหลักจากอาร์เรย์ด้วยรหัสของผู้ปกครองเช่นคุณได้รับรายการในอาร์เรย์ตามดัชนี

นี่คือตัวอย่างง่ายๆ:

const people = [
  {
    id: "12",
    parentId: "0",
    text: "Man",
    level: "1",
    children: null
  },
  {
    id: "6",
    parentId: "12",
    text: "Boy",
    level: "2",
    children: null
  },
  {
    id: "7",
    parentId: "12",
    text: "Other",
    level: "2",
    children: null
  },
  {
    id: "9",
    parentId: "0",
    text: "Woman",
    level: "1",
    children: null
  },
  {
    id: "11",
    parentId: "9",
    text: "Girl",
    level: "2",
    children: null
  }
];

function toTree(arr) {
  let arrMap = new Map(arr.map(item => [item.id, item]));
  let tree = [];

  for (let i = 0; i < arr.length; i++) {
    let item = arr[i];

    if (item.parentId !== "0") {
      let parentItem = arrMap.get(item.parentId);

      if (parentItem) {
        let { children } = parentItem;

        if (children) {
          parentItem.children.push(item);
        } else {
          parentItem.children = [item];
        }
      }
    } else {
      tree.push(item);
    }
  }

  return tree;
}

let tree = toTree(people);

console.log(tree);

แก้ไข crazy-williams-glgj3


1
แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป - จากรีวิว
JeffRS ใน

ตกลงเพิ่มแนวคิดหลักและให้ตัวอย่าง
Yusufbek

0

จากคำตอบของ @ FurkanOฉันได้สร้างเวอร์ชันอื่นที่ไม่กลายพันธุ์ของข้อมูลต้นฉบับ (เช่น @ Dac0d3r ร้องขอ) ฉันชอบคำตอบของ @shekhardtuมาก แต่รู้ว่าต้องกรองข้อมูลหลายครั้ง ฉันคิดว่าวิธีแก้ปัญหาคือการใช้คำตอบของ FurkanO โดยการคัดลอกข้อมูลก่อน ฉันลองใช้เวอร์ชันของฉันในjsperfและผลลัพธ์ที่น่าเสียดาย (มาก) เยือกเย็น ... ดูเหมือนว่าคำตอบที่ได้รับการยอมรับนั้นดีจริงๆ! เวอร์ชันของฉันค่อนข้างกำหนดค่าได้และไม่ปลอดภัยดังนั้นฉันจึงแชร์กับพวกคุณอยู่ดี นี่คือผลงานของฉัน:

function unflat(data, options = {}) {
    const { id, parentId, childrenKey } = {
        id: "id",
        parentId: "parentId",
        childrenKey: "children",
        ...options
    };
    const copiesById = data.reduce(
        (copies, datum) => ((copies[datum[id]] = datum) && copies),
        {}
    );
    return Object.values(copiesById).reduce(
        (root, datum) => {
            if ( datum[parentId] && copiesById[datum[parentId]] ) {
                copiesById[datum[parentId]][childrenKey] = [ ...copiesById[datum[parentId]][childrenKey], datum ];
            } else {
                root = [ ...root, datum ];
            }
            return root
        }, []
    );
}

const data = [
    {
        "account": "10",
        "name": "Konto 10",
        "parentAccount": null
    },{
        "account": "1010",
        "name": "Konto 1010",
        "parentAccount": "10"
    },{
        "account": "10101",
        "name": "Konto 10101",
        "parentAccount": "1010"
    },{
        "account": "10102",
        "name": "Konto 10102",
        "parentAccount": "1010"
    },{
        "account": "10103",
        "name": "Konto 10103",
        "parentAccount": "1010"
    },{
        "account": "20",
        "name": "Konto 20",
        "parentAccount": null
    },{
        "account": "2020",
        "name": "Konto 2020",
        "parentAccount": "20"
    },{
        "account": "20201",
        "name": "Konto 20201",
        "parentAccount": "2020"
    },{
        "account": "20202",
        "name": "Konto 20202",
        "parentAccount": "2020"
    }
];

const options = {
    id: "account",
    parentId: "parentAccount",
    childrenKey: "children"
};

console.log(
    "Hierarchical tree",
    unflat(data, options)
);

ด้วยพารามิเตอร์ options คุณสามารถกำหนดค่าคุณสมบัติที่จะใช้เป็น id หรือ parent id นอกจากนี้ยังสามารถกำหนดค่าชื่อของคุณสมบัติเด็กได้หากมีใครต้องการ"childNodes": []หรือบางสิ่งบางอย่าง

OP สามารถใช้ตัวเลือกเริ่มต้น:

input.People = unflat(input.People);

หาก ID หลักคือ falsy ( null, undefinedหรืออื่น ๆ ที่ค่า falsy) หรือวัตถุแม่ไม่ได้อยู่ที่เราพิจารณาวัตถุที่เป็นโหนดราก


-1
  1. ไม่มีไลบรารีของบุคคลที่สาม
  2. ไม่จำเป็นต้องสั่งอาร์เรย์ล่วงหน้า
  3. คุณจะได้รับส่วนใดส่วนหนึ่งของต้นไม้ที่คุณต้องการ

ลองทำตามนี้

function getUnflatten(arr,parentid){
  let output = []
  for(const obj of arr){
    if(obj.parentid == parentid)

      let children = getUnflatten(arr,obj.id)

      if(children.length){
        obj.children = children
      }
      output.push(obj)
    }
  }

  return output
 }

ทดสอบบน Jsfiddle


-1

แปลงโหนด Array เป็น Tree

ฟังก์ชัน ES6เพื่อแปลง Array ของโหนด (เกี่ยวข้องกับID หลัก ) - เป็นโครงสร้าง Tree:

/**
 * Convert nodes list related by parent ID - to tree.
 * @syntax getTree(nodesArray [, rootID [, propertyName]])
 *
 * @param {Array} arr   Array of nodes
 * @param {integer} id  Defaults to 0
 * @param {string} p    Property name. Defaults to "parent_id"
 * @returns {Object}    Nodes tree
 */

const getTree = (arr, p = "parent_id") => arr.reduce((o, n) => {

  if (!o[n.id]) o[n.id] = {};
  if (!o[n[p]]) o[n[p]] = {};
  if (!o[n[p]].nodes) o[n[p]].nodes= [];
  if (o[n.id].nodes) n.nodes= o[n.id].nodes;

  o[n[p]].nodes.push(n);
  o[n.id] = n;

  return o;
}, {});

สร้างรายการ HTML จากโครงสร้างโหนด

การมี Tree ของเราอยู่ในตำแหน่งนี่คือฟังก์ชันเรียกซ้ำเพื่อสร้าง UL> LI Elements:

/**
 * Convert Tree structure to UL>LI and append to Element
 * @syntax getTree(treeArray [, TargetElement [, onLICreatedCallback ]])
 *
 * @param {Array} tree Tree array of nodes
 * @param {Element} el HTMLElement to insert into
 * @param {function} cb Callback function called on every LI creation
 */

const treeToHTML = (tree, el, cb) => el.append(tree.reduce((ul, n) => {
  const li = document.createElement('li');

  if (cb) cb.call(li, n);
  if (n.nodes?.length) treeToHTML(n.nodes, li, cb);

  ul.append(li);
  return ul;
}, document.createElement('ul')));

เวลาสาธิต

นี่คือตัวอย่างที่มีอาร์เรย์เชิงเส้นของโหนดและใช้ทั้งสองฟังก์ชันข้างต้น

const getTree = (arr, p = "parent_id") => arr.reduce((o, n) => {
  if (!o[n.id]) o[n.id] = {};
  if (!o[n[p]]) o[n[p]] = {};
  if (!o[n[p]].nodes) o[n[p]].nodes = [];
  if (o[n.id].nodes) n.nodes = o[n.id].nodes;
  o[n[p]].nodes.push(n);
  o[n.id] = n;
  return o;
}, {});


const treeToHTML = (tree, el, cb) => el.append(tree.reduce((ul, n) => {
  const li = document.createElement('li');
  if (cb) cb.call(li, n);
  if (n.nodes?.length) treeToHTML(n.nodes, li, cb);
  ul.append(li);
  return ul;
}, document.createElement('ul')));


// DEMO TIME:

const nodesList = [
  {id: 10,  parent_id: 4,  text: "Item 10"}, // PS: Order does not matters
  {id: 1,   parent_id: 0,  text: "Item 1"},  
  {id: 4,   parent_id: 0,  text: "Item 4"},
  {id: 3,   parent_id: 5,  text: "Item 3"},
  {id: 5,   parent_id: 4,  text: "Item 5"},
  {id: 2,   parent_id: 1,  text: "Item 2"},
];
const myTree = getTree(nodesList)[0].nodes; // Get nodes of Root (0)

treeToHTML(myTree, document.querySelector("#tree"), function(node) {
  this.textContent = `(${node.parent_id} ${node.id}) ${node.text}`;
  this._node = node;
  this.addEventListener('click', clickHandler);
});

function clickHandler(ev) {
  if (ev.target !== this) return;
  console.clear();
  console.log(this._node.id);
};
<div id="tree"></div>


-1

นี่เป็นกระทู้เก่า แต่ฉันคิดว่าการอัปเดตไม่เคยทำให้เกิดปัญหาใด ๆ ด้วย ES6 คุณสามารถทำได้:

const data = [{
    id: 1,
    parent_id: 0
}, {
    id: 2,
    parent_id: 1
}, {
    id: 3,
    parent_id: 1
}, {
    id: 4,
    parent_id: 2
}, {
    id: 5,
    parent_id: 4
}, {
    id: 8,
    parent_id: 7
}, {
    id: 9,
    parent_id: 8
}, {
    id: 10,
    parent_id: 9
}];

const arrayToTree = (items=[], id = null, link = 'parent_id') => items.filter(item => id==null ? !items.some(ele=>ele.id===item[link]) : item[link] === id ).map(item => ({ ...item, children: arrayToTree(items, item.id) }))
const temp1=arrayToTree(data)
console.log(temp1)

const treeToArray = (items=[], key = 'children') => items.reduce((acc, curr) => [...acc, ...treeToArray(curr[key])].map(({ [`${key}`]: child, ...ele }) => ele), items);
const temp2=treeToArray(temp1)

console.log(temp2)

หวังว่ามันจะช่วยใครบางคน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.