-
Notifications
You must be signed in to change notification settings - Fork 30
/
index.js
109 lines (93 loc) · 2.5 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
'use strict';
var property = require('nested-property');
var keyBy = require('lodash.keyby');
function createTree(array, rootNodes, customID, childrenProperty) {
var tree = [];
for (var rootNode in rootNodes) {
var node = rootNodes[rootNode];
var childNode = array[node[customID]];
if (!node && !rootNodes.hasOwnProperty(rootNode)) {
continue;
}
if (childNode) {
node[childrenProperty] = createTree(
array,
childNode,
customID,
childrenProperty
);
}
tree.push(node);
}
return tree;
}
function groupByParents(array, options) {
var arrayByID = keyBy(array, options.customID);
return array.reduce(function(prev, item) {
var parentID = property.get(item, options.parentProperty);
if (!parentID || !arrayByID.hasOwnProperty(parentID)) {
parentID = options.rootID;
}
if (parentID && prev.hasOwnProperty(parentID)) {
prev[parentID].push(item);
return prev;
}
prev[parentID] = [item];
return prev;
}, {});
}
function isObject(o) {
return Object.prototype.toString.call(o) === '[object Object]';
}
function deepClone(data) {
if (Array.isArray(data)) {
return data.map(deepClone);
} else if (isObject(data)) {
return Object.keys(data).reduce(function(o, k) {
o[k] = deepClone(data[k]);
return o;
}, {});
} else {
return data;
}
}
/**
* arrayToTree
* Convert a plain array of nodes (with pointers to parent nodes) to a nested
* data structure
*
* @name arrayToTree
* @function
*
* @param {Array} data An array of data
* @param {Object} options An object containing the following fields:
*
* - `parentProperty` (String): A name of a property where a link to
* a parent node could be found. Default: 'parent_id'
* - `customID` (String): An unique node identifier. Default: 'id'
* - `childrenProperty` (String): A name of a property where children nodes
* are going to be stored. Default: 'children'.
*
* @return {Array} Result of transformation
*/
module.exports = function arrayToTree(data, options) {
options = Object.assign(
{
parentProperty: 'parent_id',
childrenProperty: 'children',
customID: 'id',
rootID: '0'
},
options
);
if (!Array.isArray(data)) {
throw new TypeError('Expected an array but got an invalid argument');
}
var grouped = groupByParents(deepClone(data), options);
return createTree(
grouped,
grouped[options.rootID],
options.customID,
options.childrenProperty
);
};