[英]Lodash - How to create a tree from a flat array
Every time my application loads, I receive the following json
: 每当我的应用程序加载时,我都会收到以下
json
:
[
{
id: 'mALRRY93jASr',
identifier: '100',
text: 'Text A'
},
{
id: '7S3xHZEdNcfV',
identifier: '200',
text: 'Text B'
},
{
id: '2ZA5xSJeukU6',
identifier: '300',
text: 'Text C',
},
{
id: 'bhg3GnLEvw2k',
identifier: '300.100',
text: 'Text C - A'
},
{
id: 'bhg3GnLEvw2k',
identifier: '300.100.100',
text: 'Text C - A - A'
},
{
id: '2AcXNr4HT388',
identifier: '300.200',
text: 'Text C - B'
}
]
The tree levels are identified by the identifier
property. 树级别由
identifier
属性identifier
。
The tree can have thousands of children, so it needs to be recursive. 该树可以有数千个孩子,因此它需要递归。
How can I arrange the json
using Lodash to looks like the following json
? 我如何使用Lodash排列
json
使其看起来像以下json
?
[
{
id: 'mALRRY93jASr',
identifier: '100',
text: 'Text A'
},
{
id: '7S3xHZEdNcfV',
identifier: '200',
text: 'Text B'
},
{
id: '2ZA5xSJeukU6',
identifier: '300',
text: 'Text C',
children: [
{
id: 'bhg3GnLEvw2k',
identifier: '300.100',
text: 'Text C - A',
children: [
{
id: 'bhg3GnLEvw2k',
identifier: '300.100.100',
text: 'Text C - A - A'
}
]
},
{
id: '2AcXNr4HT388',
identifier: '300.200',
text: 'Text C - B'
}
]
}
]
You could take an iterative approach by looking for objects in the same path of identifier
and build a nested structure. 您可以通过在同一
identifier
路径中查找对象并构建嵌套结构来采用迭代方法。
This approach works for unsorted data as well. 这种方法也适用于未排序的数据。
var data = [{ id: 'mALRRY93jASr', identifier: '100', text: 'Text A' }, { id: '7S3xHZEdNcfV', identifier: '200', text: 'Text B' }, { id: '2ZA5xSJeukU6', identifier: '300', text: 'Text C' }, { id: 'bhg3GnLEvw2k', identifier: '300.100', text: 'Text C - A' }, { id: 'bhg3GnLEvw2k', identifier: '300.100.100', text: 'Text C - A - A' }, { id: '2AcXNr4HT388', identifier: '300.200', text: 'Text C - B' }], tree = []; data.reduce((r, o) => { o.identifier .split('.') .map((_, i, a) => a.slice(0, i + 1).join('.')) .reduce((q, identifier, i, { length }) => { var temp = (q.children = q.children || []).find(p => p.identifier === identifier); if (!temp) { q.children.push(temp = { identifier }); } if (i + 1 === length) { Object.assign(temp, o); } return temp; }, r); return r; }, { children: tree }); console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I used a previous answer of mine as a base. 我以我以前的答案为基础。 There are a lot of similarities, but your "path" syntax is a little different and I had to tweak some of the parsing.
有很多相似之处,但是您的“路径”语法有点不同,因此我不得不调整一些解析。
const data = [
{
id: 'mALRRY93jASr',
identifier: '100',
text: 'Text A'
},
{
id: '7S3xHZEdNcfV',
identifier: '200',
text: 'Text B'
},
{
id: '2ZA5xSJeukU6',
identifier: '300',
text: 'Text C',
},
{
id: 'bhg3GnLEvw2k',
identifier: '300.100',
text: 'Text C - A'
},
{
id: 'bhg3GnLEvw2k',
identifier: '300.100.100',
text: 'Text C - A - A'
},
{
id: '2AcXNr4HT388',
identifier: '300.200',
text: 'Text C - B'
}
];
const pathPartRegex = /.*?\./g;
const tree = _.reduce(data, (result, value) => {
// We use the . character as a "path part" terminator,
// but it is not there at the end of the string, so we add it
let identifier = value.identifier;
if (!identifier.endsWith(".")) {
identifier = identifier + ".";
}
const pathParts = identifier.match(pathPartRegex);
let node = result;
let path = "";
// Go down through tree until last path part
const notLastPart = pathParts.splice(0, pathParts.length - 1);
for (const pathPart of notLastPart) {
path += pathPart;
const existingNode = node.children
? node.children.find(item => path.startsWith(item.identifier) )
: node.find(item => path.startsWith(item.identifier));
if (existingNode) {
node = existingNode
} else {
// If we need to traverse over a path that doesn't exist, just create it
// See notes
const newNode = {
identifier: path,
children: []
};
// The root element is just an array, and doesn't have a children property
if (node.children) {
node.children.push(newNode);
} else {
node.push(newNode);
}
node = newNode;
}
}
// Add new node
const newNode = {
id: value.id,
text: value.text,
identifier: value.identifier,
children: []
};
// The root element is just an array, and doesn't have a children property
if (node.children) {
node.children.push(newNode);
} else {
node.push(newNode);
}
return result;
}, []);
Tested via RunKit ( https://npm.runkit.com/lodash ) 通过RunKit( https://npm.runkit.com/lodash )测试
Notes: 笔记:
The same warnings from the original answer also apply here. 原始答案中的相同警告也适用于此。
You can use Array.reduce()
and _.setWith()
to create a tree of objects by the path (identity). 您可以使用
Array.reduce()
和_.setWith()
通过路径(身份)创建对象树。 Then you can use a recursive function with _.transform()
to convert the children
to an array using _.values()
: 然后你可以使用一个递归函数
_.transform()
的转换children
使用到一个数组_.values()
const createTree = (arr) => { // reduce to a tree of objects const oTree = arr.reduce((r, o) => { const key = o.identifier.replace(/\\./g, '.children.'); // creates the path and adds the object value return _.setWith(r, key, o, Object) }, {}); // transforms the children to an array recursivly const transformChildren = (tree) => _.transform(tree, (acc, v, k) => { const value = _.isObject(v) ? transformChildren(v) : v; acc[k] = _.eq(k, 'children') ? _.values(value) : value; }); return transformChildren(_.values(oTree)); }; const data = [{"id":"mALRRY93jASr","identifier":"100","text":"Text A"},{"id":"7S3xHZEdNcfV","identifier":"200","text":"Text B"},{"id":"2ZA5xSJeukU6","identifier":"300","text":"Text C"},{"id":"bhg3GnLEvw2k","identifier":"300.100","text":"Text C - A"},{"id":"bhg3GnLEvw2k","identifier":"300.100.100","text":"Text C - A - A"},{"id":"2AcXNr4HT388","identifier":"300.200","text":"Text C - B"}]; const result = createTree(data); console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.