Xây dựng mảng cây từ mảng phẳng trong javascript


134

Tôi có một tệp json phức tạp mà tôi phải xử lý bằng javascript để làm cho nó được phân cấp, để sau này xây dựng một cây. Mỗi mục nhập của json có: id: id duy nhất, ParentId: id của nút cha (là 0 nếu nút là gốc của cây): mức độ sâu trong cây

Dữ liệu json đã được "đặt hàng". Tôi có nghĩa là một mục nhập sẽ có trên chính nút cha hoặc nút anh em và bên dưới là nút con hoặc nút anh em.

Đầu vào :

{
    "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
        },
    ]
}

Sản lượng dự kiến:

{
    "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
Có một số cách để làm điều đó, bạn đã thử bất cứ điều gì chưa?
bfavaretto

Tôi giả định rằng một parentIdsố 0phương tiện không có id phụ huynh và nên là lớp trên cùng.
Donnie D'Amato

Thông thường các loại nhiệm vụ yêu cầu đối tượng kiến ​​thức làm việc rộng rãi. Câu hỏi hay
Gangadhar JANNU

Câu trả lời:


156

Có một giải pháp hiệu quả nếu bạn sử dụng tra cứu bản đồ. Nếu cha mẹ luôn đến trước con cái, bạn có thể hợp nhất hai vòng lặp. Nó hỗ trợ nhiều rễ. Nó đưa ra một lỗi trên các nhánh lơ lửng, nhưng có thể được sửa đổi để bỏ qua chúng. Nó không yêu cầu thư viện của bên thứ 3. Đó là, theo như tôi có thể nói, giải pháp nhanh nhất.

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ếu bạn theo lý thuyết phức tạp thì giải pháp này là Θ (n log (n)). Giải pháp bộ lọc đệ quy là (n ^ 2) có thể là một vấn đề đối với các tập dữ liệu lớn.


28
Hãy nhớ rằng với giải pháp này, các nút của bạn phải được sắp xếp cụ thể để đảm bảo cha mẹ được đẩy vào bản đồ trước, nếu không quá trình tra cứu sẽ xảy ra lỗi ... vì vậy bạn cần phải sắp xếp em theo thuộc tính cấp hoặc bạn cần để đẩy chúng vào bản đồ trước. và sử dụng một vòng lặp for riêng cho việc tra cứu. (tôi thích sắp xếp tuy nhiên khi bạn không có thuộc tính cấp độ, các vòng lặp riêng biệt có thể là một tùy chọn)
Sander

Ban đầu tôi thấy ngạc nhiên khi có thêm thông tin, ví dụ: một đường dẫn như [1, 5, 6] trong đó mảng là tổ tiên tiếp theo, không thể được sử dụng hiệu quả trong đó. Nhưng nhìn vào mã nó có vẻ hợp lý vì tôi tin rằng đó là O (n)
Ced

1
Mặc dù câu trả lời tốt, nó là phức tạp. Áp dụng câu trả lời của tôi cho chỉ hai mã dòng: liên kết
Iman Bahrampour

Xin vui lòng giải thích tại sao giải pháp này là Θ (n log (n)), dường như nó đang mất thời gian O (n).
amrender singh

@amrendersingh bên trong vòng lặp for là một tra cứu băm trong mapđó (về lý thuyết) là O (log n).
Halcyon

72

Như được đề cập bởi @Sander, câu trả lời của @ Halcyon giả sử một mảng được sắp xếp trước, sau đây thì không. (Tuy nhiên, giả sử bạn đã tải underscore.js - mặc dù nó có thể được viết bằng javilla 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>

Yêu cầu

Nó giả sử các thuộc tính 'id' và 'Parentid' chỉ ra ID và ID cha tương ứng. Phải có các phần tử với ID cha 0, nếu không bạn sẽ nhận được một mảng trống. Các phần tử mồ côi và con cháu của họ bị 'mất'

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


4
Bạn có thể thêm else { parent['children'] = []; }sau mệnh đề if đầu tiên để đảm bảo rằng mọi nút đều có thuộc tính children(nó sẽ trống nếu nút là nút lá)
Christopher

2
Đoạn mã của bạn đã hoạt động hoàn hảo, cảm ơn bạn !! Điều duy nhất là: treekhông bao giờ được thông qua như một đối số khi gọi hàm một cách đệ quy, vì vậy tôi nghĩ rằng dòng này tree = typeof tree !== 'undefined' ? tree : [];có thể được thay thế bằnglet tree = [];
Oscar Calderon

điều này có thể được sửa đổi để cho phép nullParent_ids thay vì 0 không? Chỉnh sửa: Nevermind, tôi đã làm cho nó hoạt động bằng cách thay đổi id: 0thành id: null.
dlinx90

Hãy nhớ rằng câu trả lời trên sử dụng hai vòng lặp, và do đó có thể được cải thiện. Vì tôi không thể tìm thấy mô-đun npm thực hiện giải pháp O (n), tôi đã tạo một mô-đun sau (đơn vị được kiểm tra, độ bao phủ mã 100%, kích thước chỉ 0,5 kb và bao gồm các kiểu chữ). Có lẽ nó giúp được ai đó: npmjs.com/package/performant-array-to-tree
Philip Stanislaus

4
Đối với bất cứ ai quan tâm, mã có thể dễ dàng chuyển đổi sang js vani: jsfiddle.net/LkkwH/853
XEC

48

(TIỀN THƯỞNG1: NODES CÓ THỂ hoặc CÓ THỂ KHÔNG ĐƯỢC ĐẶT HÀNG)

(TIỀN THƯỞNG2: KHÔNG CẦN THƯ VIỆN BÊN 3RD, NHẬP KHẨU

(BONUS3: Người dùng "Elias Rabl" nói rằng đây là giải pháp nhanh nhất, xem câu trả lời của anh ấy bên dưới)

Đây là:

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
}

Đây là một thử nghiệm, nó có thể giúp bạn hiểu cách giải pháp hoạt động:

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)
});

2
Sẽ không chính xác hơn nếu chúng ta childNodeschỉ thêm khi cần? Bằng cách loại bỏ chúng từ cái đầu tiên forEachvà di chuyển chúng bên trong cái thứ hai?
arpl

@arpl đồng ý. Người ta có thể dễ dàng thay đổi điều đó nếu được yêu cầu. Hoặc nếu bạn nghĩ nó phải là cách mặc định, tôi có thể thay đổi nó.
FurkanO

@FurkanO giải pháp thực sự tốt, tuy nhiên sẽ có thể đến bất kỳ nơi nào gần hiệu suất này với lập trình chức năng (không có đột biến)
Dac0d3r

34

Có cùng một vấn đề, nhưng tôi không thể chắc chắn rằng dữ liệu đã được sắp xếp hay chưa . Tôi không thể sử dụng thư viện của bên thứ 3 vì vậy đây chỉ là vanilla Js; Dữ liệu đầu vào có thể được lấy từ ví dụ của @ 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, " "))

Fiddle

Mảng phẳng để cây


trong một số trường hợp mappedArr[mappedElem['parentid']]['children']đã thất bại vì không thể truy cập vào childrenkhông xác định.
Al-Mothafar

Làm thế nào tôi có thể bắt đầu tại id cha: 1?
vinni

31

Sử dụng phương pháp ES6 này. Hoạt động như quyến rũ

// 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
Câu trả lời ngắn nhất và hay nhất tôi nghĩ
java-man-script

2
sloooow so với câu trả lời của FurkanO
Geza Turi

16

một chức năng đơn giản hơn list-to-tree-lite

npm install list-to-tree-lite

listToTree(list)

nguồn:

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

Bạn có thể xử lý câu hỏi này chỉ với mã hóa hai dòng:

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

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

Kiểm tra trực tuyến (xem bảng điều khiển trình duyệt cho cây được tạo)

Yêu cầu:

1- Cài đặt lodash 4 (thư viện Javascript để thao tác các đối tượng và bộ sưu tập bằng các phương thức biểu diễn => như Linq trong c #) Lodash

2- Một FlatArray như dưới đây:

    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:[]
    }];

Cảm ơn ông Bakhshabadi

Chúc may mắn


8

Nó có thể là gói cài đặt list-to-tree hữu ích :

bower install list-to-tree --save

hoặc là

npm install list-to-tree --save

Ví dụ: có danh sách:

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
  }
];

Sử dụng gói list-to-tree:

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

Kết quả:

[{
  "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
Lưu ý rằng các câu trả lời chỉ liên kết không được khuyến khích, các câu trả lời SO phải là điểm cuối của tìm kiếm giải pháp (so với một điểm dừng khác của tài liệu tham khảo, có xu hướng bị cũ theo thời gian). Vui lòng xem xét việc thêm một bản tóm tắt độc lập ở đây, giữ liên kết làm tài liệu tham khảo
kleopatra

Tôi không hiểu tại sao -1, tôi nghĩ rằng đó là một giải pháp tốt nhưng tiếc là tôi không tìm thấy gói trong gitHub hoặc trong một kho lưu trữ công cộng khác
oriaj

Cảm ơn bạn đã quan tâm đến gói. Tôi dự định sau này sẽ mở rộng nó. Đây là một liên kết đến kho lưu trữ github.com/DenQ/list-to-tree
DenQ

@oriaj Tôi rất vui vì lợi ích của dự án. Kế hoạch của một vài ý tưởng
DenQ

Hoạt động độc đáo, cảm ơn bạn @DenQ. Muốn nó có phạm vi kiểm tra nhiều hơn mặc dù!
IliasT

3

Tôi đã viết một kịch bản thử nghiệm để đánh giá hiệu suất của hai giải pháp chung nhất (có nghĩa là đầu vào không phải được sắp xếp trước và mã không phụ thuộc vào thư viện của bên thứ ba), được đề xuất bởi người dùng shekhardtu ( xem câu trả lời ) và FurkanO ( xem câu trả lời ).

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

Giải pháp của FurkanO dường như là nhanh nhất.

/*
** 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

Đây là một đề xuất cho các mục không có thứ tự. Hàm này hoạt động với một vòng lặp duy nhất và với bảng băm và thu thập tất cả các mục với chúng id. Nếu một nút gốc được tìm thấy, thì đối tượng được thêm vào mảng kết quả.

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

cũng làm điều đó với lodashjs (v4.x)

function buildTree(arr){
  var a=_.keyBy(arr, 'id')
  return _
   .chain(arr)
   .groupBy('parentId')
   .forEach(function(v,k){ 
     k!='0' && (a[k].children=(a[k].children||[]).concat(v));
   })
   .result('0')
   .value();
}

1

Tôi thích giải pháp JavaScript thuần túy của @ WilliamLeung, nhưng đôi khi bạn cần thực hiện các thay đổi trong mảng hiện có để giữ tham chiếu đến đối tượng.

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;
	}
}


Tôi đã tạo hàm này để chuyển đổi dữ liệu từ mảng đối tượng sang cấu trúc cây, cần thiết cho biểu đồ tương tác cây d3. Chỉ với 40 dòng mã tôi đã có thể nhận được đầu ra. Tôi đã viết chức năng này một cách hiệu quả sử dụng chức năng đệ quy đệ quy trong js. Hãy thử và cho tôi biết phản hồi của bạn. Cảm ơn bạn!!!!
karthik reddy

Cảm ơn vì anwser..Có hoạt động hoàn hảo cho cấu trúc liên kết cây d3 của tôi .. Bây giờ tôi có yêu cầu rằng tôi cần thay đổi màu nút dựa trên các giá trị của nút..Vì vậy, tôi cần phải chuyển một giá trị cờ trong JSON . Làm thế nào để tôi làm điều đó .. {"name": "Cấp cao nhất", "cờ": 1, "cha mẹ": "null", "trẻ em": [{"tên": "Ấn Độ", "cờ": 0 , "phụ huynh": "Cấp cao nhất", "trẻ em": [
Puneeth Kumar

1

Bạn có thể xem qua gói npm cây sử dụng Nó cũng có thể rất tiện dụng nếu bạn muốn tải dữ liệu từ bảng dữ liệu SQL DB. Bạn cũng có thể dễ dàng thêm dữ liệu bổ sung vào các nút trong cây được tạo.

Từ chối trách nhiệm, tôi đã thực hiện gói này.


1

Tôi đã gặp vấn đề tương tự vài ngày trước khi phải hiển thị cây thư mục từ mảng phẳng. Tôi không thấy bất kỳ giải pháp nào trong TypeScript ở đây vì vậy tôi hy vọng nó sẽ hữu ích.

Trong trường hợp của tôi, cha mẹ chính chỉ là một, còn mảng rawData không phải sắp xếp. Giải pháp dựa trên việc chuẩn bị đối tượng tạm thời như {parentId: [child1, child2, ...] }

ví dụ dữ liệu thô

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 của thư mục

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 || [];
    }
}

GIẢI PHÁP : Hàm trả về cấu trúc cây cho đối số phẳng

    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

Đây là một hàm trợ giúp đơn giản mà tôi đã tạo được mô hình hóa sau các câu trả lời ở trên, được điều chỉnh theo môi trường 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

Đây là một phiên bản sửa đổi của Steven Harris 'là ES5 đơn giản và trả về một đối tượng được khóa trên id thay vì trả về một mảng các nút ở cả cấp cao nhất và cho trẻ em.

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

Đây là phiên bản sửa đổi ở trên hoạt động với nhiều mục gốc, tôi sử dụng GUID cho id và cha mẹ của mình để trong giao diện người dùng tạo ra chúng, tôi mã hóa các mục gốc mã hóa thành thứ gì đó như 0000000-00000-00000-TREE-ROOT-ITEM

var cây = unflatten (bản ghi, "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

Sao chép từ Internet 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

Bạn có thể sử dụng gói npm mảng-to-tree https://github.com/alferov/array-to-tree . Nó chuyển đổi một mảng đơn giản các nút (có con trỏ thành nút cha) thành cấu trúc dữ liệu lồng nhau.

Giải quyết vấn đề với việc chuyển đổi lấy từ cơ sở dữ liệu tập hợp dữ liệu sang cấu trúc dữ liệu lồng nhau (tức là cây điều hướng).

Sử dụng:

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

đây là những gì tôi đã sử dụng trong một dự án phản ứng

// 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 }),
}));

sử dụng:

// 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');

đầu ra:

[
   {
      "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

Giải pháp bản in của tôi, có thể nó giúp bạn:

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;
}

Ví dụ về cách sử dụng:

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

0

Tôi đã viết một phiên bản ES6 dựa trên câu trả lời @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));

Nguyên tắc của thuật toán này là sử dụng "bản đồ" để thiết lập mối quan hệ chỉ mục. Rất dễ tìm thấy "mục" trong danh sách bằng "ParentId" và thêm "con" vào mỗi "mục", vì "danh sách" là mối quan hệ tham chiếu, vì vậy "gốc" sẽ Xây dựng mối quan hệ với toàn bộ cây.


0

Trả lời cho một câu hỏi tương tự:

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

CẬP NHẬT

Bạn có thể sử dụng Mapđối tượng được giới thiệu trong ES6. Về cơ bản thay vì tìm cha mẹ bằng cách lặp lại mảng đó, bạn sẽ chỉ lấy mục cha mẹ từ mảng theo id của cha mẹ giống như bạn lấy các mục trong một mảng theo chỉ mục.

Đây là ví dụ đơn giản:

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);

Chỉnh sửa crazy-williams-glgj3


1
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi. - Từ đánh giá
JeffRSon

Ok, Đã thêm ý chính và đưa ra một ví dụ mẫu,
Yusufbek

0

Dựa trên câu trả lời của @ FurkanO , tôi đã tạo một phiên bản khác không làm thay đổi dữ liệu gốc (như @ Dac0d3r yêu cầu). Tôi thực sự thích câu trả lời của @ shekhardtu , nhưng nhận ra rằng nó phải lọc qua dữ liệu nhiều lần. Tôi nghĩ rằng một giải pháp có thể là sử dụng câu trả lời của FurkanO bằng cách sao chép dữ liệu trước. Tôi đã thử phiên bản của mình trong jsperf , và kết quả không may (rất) ảm đạm ... Có vẻ như câu trả lời được chấp nhận thực sự là một câu trả lời hay! Phiên bản của tôi khá cấu hình và không an toàn, vì vậy tôi vẫn chia sẻ nó với các bạn; đây là đóng góp của tôi:

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)
);

Với tham số tùy chọn, có thể định cấu hình thuộc tính nào sẽ sử dụng làm id hoặc id cha. Cũng có thể định cấu hình tên của tài sản trẻ em, nếu ai đó muốn "childNodes": []hoặc một cái gì đó.

OP chỉ có thể sử dụng các tùy chọn mặc định:

input.People = unflat(input.People);

Nếu id mẹ là falsy ( null, undefinedhoặc khác giá trị falsy) hoặc đối tượng phụ huynh không tồn tại, chúng ta xem xét các đối tượng là một nút gốc.


-1
  1. không có thư viện bên thứ ba
  2. không cần mảng đặt hàng trước
  3. bạn có thể lấy bất kỳ phần nào của cây bạn muốn

Thử cái này

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
 }

Kiểm tra nó trên Jsfiddle


-1

Chuyển đổi các nút Array thành Tree

Hàm ES6 để chuyển đổi một mảng các nút (liên quan đến ID cha ) - thành cấu trúc 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;
}, {});

Tạo danh sách HTML từ các nút Cây

Có Cây của chúng ta, đây là một hàm đệ quy để xây dựng các phần tử UL> LI:

/**
 * 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')));

Thời gian demo

Dưới đây là một ví dụ có Mảng các nút tuyến tính và sử dụng cả hai hàm trên:

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

Đây là một chủ đề cũ nhưng tôi nghĩ rằng một bản cập nhật không bao giờ bị tổn thương, với ES6 bạn có thể làm:

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)

hy vọng nó sẽ giúp được ai đó

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.