递归地深度压扁 JavaScript Object
Deep Flatten JavaScript Object Recursively
数据:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
代码:
var fdr = [];
var fd = function(n) {
if (n.items) {
_.forEach(n.items, function (value){
fd(value);
});
}
fdr.push(n);
};
_.forEach(data, fd);
console.log(fdr);
期望输出:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
];
条件:
- Object 有未知级别。有些 child
item
可能下降一级,有些可能下降 5 级。
问题
代码中的fd
函数是我想出来的。我相信有一种 'cleaner' 方法可以做到这一点,只是想不出什么。另外,函数 return items
object,将其渲染为循环 object.
JsBin:
http://jsbin.com/debojiqove/2/edit?html,js,output
有没有办法用 lodash 或简单的 JavaScript 递归地展平 object?
这是使用递归函数的解决方案,我称之为 flattenNestedObjectsArray()
(对于 native JavaScript):
function flattenNestedObjectsArray(arr, part){
var flattened = part || [], items;
arr.forEach(function(v){
if (Array.isArray(v.items) && v.items.length) {
items = v.items;
v.items = [];
flattened.push(v);
flattened.concat(flattened, flattenNestedObjectsArray(items, flattened));
} else {
flattened.push(v);
}
});
return flattened;
}
var flattened = flattenNestedObjectsArray(data);
console.log(JSON.stringify(flattened, 0, 4));
console.log
输出:
[
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
关于项目的简单 Javascript 解决方案。它不会改变源数组。
function flat(r, a) {
var b = {};
Object.keys(a).forEach(function (k) {
if (k !== 'items') {
b[k] = a[k];
}
});
r.push(b);
if (Array.isArray(a.items)) {
b.items = a.items.map(function (a) { return a.id; });
return a.items.reduce(flat, r);
}
return r;
}
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
document.write('<pre>' + JSON.stringify(data.reduce(flat, []), 0, 4) + '</pre>');
这是我的递归 flattenItems 函数版本。
请注意,我已经删除了最终结果中所有级别的 items 属性。
function flattenItems(data) {
// flat is the array that we will return by the end
var flat = [];
data.forEach(function(item) {
// get child properties only
var flatItem = {};
Object.keys(item).forEach(function(key) {
if(item[key] && item.hasOwnProperty(key) && !Array.isArray(item[key])) {
flatItem[key] = item[key];
}
// recursive flattern on subitems
// add recursive call results to the
// current stack version of "flat", by merging arrays
else if(Array.isArray(item[key])) {
Array.prototype.push.apply(flat, flattenItems(item[key]));
}
});
flat.push(flatItem);
});
// sort by level before returning
return flat.sort(function(i1, i2) {
return parseInt(i1.level) - parseInt(i2.level);
});
}
这里是 fiddle 使用您的样本数据,检查控制台。
普通JavaScript
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
var r = [];
function flatten(a) {
if (a.length == 0) return;
var o = {};
o.id = a[0].id;
o.level = a[0].level;
o.text = a[0].text;
o.type = a[0].type
o.items = a[0].items == null ? null : []
r.push(o);
if (Array.isArray(a[0].items)) {
flatten(a[0].items);
}
a.shift();
flatten(a);
}
flatten(data);
document.write('<pre>' + JSON.stringify(r, 0, 2) + '</pre>');
有点ES6的味道
function flatten(xs) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
if (x.items) {
acc = acc.concat(flatten(x.items));
x.items = [];
}
return acc;
}, []);
}
使用 reduce
和 recursion
的较短解决方案
function flatten(data){
return data.reduce(function(result,next){
result.push(next);
if(next.items){
result = result.concat(flatten(next.items));
next.items = [];
}
return result;
},[]);
}
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
var result = flatten(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
只有一个线性函数可以完成这项工作。
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }],
flatIron = (a,b) => a.reduce((p,c) => {!!c.items ? (p.push(c), flatIron(c.items,p), c.items = []) : p.push(c); return p},b),
flatArr = flatIron(data,[]);
document.write('<pre>' + JSON.stringify(flatArr, 0, 2) + '</pre>');
从 Lo-Dash 3.0.0 开始,_.flattenDeep(data) 将 return 一个你想要的深度扁平数组。还有一个 _.flatten(data) 函数可以浅层压平。
递归reducer函数的另一种方式
_.reduce(data, function reducer(result, val) {
var items = _.reduce(val.items, reducer, []);
val.items = _.isArray(val.items) ? [] : val.items;
return _.concat(result, val, items);
}, []);
我需要做同样的事情,在解决我的问题时找到了使用 lodash 解决你的问题的方法:
function kids(node) {
return node.items
? [{...node, items: []}, _.map(node.items, kids)]
: {...node, items: null};
}
_.flatMapDeep(data, kids);
修改了 Роман Парадеев 答案,使其更加动态。
function flatten(xs, childSelector) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
let children = childSelector(x);
if (children) {
acc = acc.concat(flatten(children, childSelector));
}
return acc;
}, []);
}
现在 items
不是硬编码的,您可以使用它 flatten(data, x => x.items)
。
使用 _.flatMapDeep
(自 Lodash 4.7 起可用):
var flatten = function(item) {
return [item, _.flatMapDeep(item.items, flatten)];
}
var result = _.flatMapDeep(data, flatten);
此解决方案应该适用于 IE11;使用过滤器、映射和减少。
var item = function(x) {
return {
"id": x.id,
"level": x.level,
"text": x.text,
"type": x.type,
"items": x.items ? [] : null
}
}
var flatten = function(a, b) {
return a.concat(b);
};
var onlyUnique = function(acc, curr) {
if (acc.length == 0) {
acc.push(curr);
} else {
var search = acc.filter(function(x){return x.id===curr.id;})
if (search.length == 0) {
acc.push(curr);
}
}
return acc;
}
var newData = data.map(function(x) {
return x.items.map(function(xx) {
return xx.items.map(function(xxx) {
return [item(x), item(xx), item(xxx)];
}).reduce(flatten, []);
}).reduce(flatten, [])
}).reduce(flatten, []).reduce(onlyUnique, []);;
console.log(JSON.stringify(newData, null, 2))
由于这个较旧的问题已被重新提出,这里是一个现代版本。它不会改变(@Nina Scholz:或破坏!)原始数据,而是 returns 包含新对象的新数组。
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [node, ... flattenItems (items || [])])
const data = [{id: 1, level: "1", text: "Sammy", type: "Item", items: [{id: 11, level: "2", text: "Table", type: "Item", items: [{id: 111, level: "3", text: "Dog", type: "Item", items: null}, {id: 112, level: "3", text: "Cat", type: "Item", items: null}]}, {id: 12, level: "2", text: "Chair", type: "Item", items: [{id: 121, level: "3", text: "Dog", type: "Item", items: null}, {id: 122, level: "3", text: "Cat", type: "Item", items: null}]}]}, {id: 2, level: "1", text: "Sundy", type: "Item", items: [{id: 21, level: "2", text: "MTable", type: "Item", items: [{id: 211, level: "3", text: "MTDog", type: "Item", items: null}, {id: 212, level: "3", text: "MTCat", type: "Item", items: null}]}, {id: 22, level: "2", text: "MChair", type: "Item", items: [{id: 221, level: "3", text: "MCDog", type: "Item", items: null}, {id: 222, level: "3", text: "MCCat", type: "Item", items: null}]}]}, {id: 3, level: "1", text: "Bruce", type: "Folder", items: [{id: 31, level: "2", text: "BTable", type: "Item", items: [{id: 311, level: "3", text: "BTDog", type: "Item", items: null}, {id: 312, level: "3", text: "BTCat", type: "Item", items: null}]}, {id: 32, level: "2", text: "Chair", type: "Item", items: [{id: 321, level: "3", text: "BCDog", type: "Item", items: null}, {id: 322, level: "3", text: "BCCat", type: "Item", items: null}]}]}]
console .log (flattenItems (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
此版本不包含相当无用的 items
属性。添加它会很容易,像这样:
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [
{... node, items: items == null ? null : []},
... flattenItems (items || [])
])
简单的 map 和 reduce :
export function flatten<T>(value: T): T[] {
const out: T[] = [];
if (value.children) {
const children = Object.values(value.children);
out.push(
value,
...children.map(flatten).reduce((acc, curVal) => {
return acc.concat(curVal);
}),
);
} else {
out.push(value);
}
return out;
}
数据:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
代码:
var fdr = [];
var fd = function(n) {
if (n.items) {
_.forEach(n.items, function (value){
fd(value);
});
}
fdr.push(n);
};
_.forEach(data, fd);
console.log(fdr);
期望输出:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
];
条件:
- Object 有未知级别。有些 child
item
可能下降一级,有些可能下降 5 级。
问题
代码中的fd
函数是我想出来的。我相信有一种 'cleaner' 方法可以做到这一点,只是想不出什么。另外,函数 return items
object,将其渲染为循环 object.
JsBin: http://jsbin.com/debojiqove/2/edit?html,js,output
有没有办法用 lodash 或简单的 JavaScript 递归地展平 object?
这是使用递归函数的解决方案,我称之为 flattenNestedObjectsArray()
(对于 native JavaScript):
function flattenNestedObjectsArray(arr, part){
var flattened = part || [], items;
arr.forEach(function(v){
if (Array.isArray(v.items) && v.items.length) {
items = v.items;
v.items = [];
flattened.push(v);
flattened.concat(flattened, flattenNestedObjectsArray(items, flattened));
} else {
flattened.push(v);
}
});
return flattened;
}
var flattened = flattenNestedObjectsArray(data);
console.log(JSON.stringify(flattened, 0, 4));
console.log
输出:
[
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
关于项目的简单 Javascript 解决方案。它不会改变源数组。
function flat(r, a) {
var b = {};
Object.keys(a).forEach(function (k) {
if (k !== 'items') {
b[k] = a[k];
}
});
r.push(b);
if (Array.isArray(a.items)) {
b.items = a.items.map(function (a) { return a.id; });
return a.items.reduce(flat, r);
}
return r;
}
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
document.write('<pre>' + JSON.stringify(data.reduce(flat, []), 0, 4) + '</pre>');
这是我的递归 flattenItems 函数版本。 请注意,我已经删除了最终结果中所有级别的 items 属性。
function flattenItems(data) {
// flat is the array that we will return by the end
var flat = [];
data.forEach(function(item) {
// get child properties only
var flatItem = {};
Object.keys(item).forEach(function(key) {
if(item[key] && item.hasOwnProperty(key) && !Array.isArray(item[key])) {
flatItem[key] = item[key];
}
// recursive flattern on subitems
// add recursive call results to the
// current stack version of "flat", by merging arrays
else if(Array.isArray(item[key])) {
Array.prototype.push.apply(flat, flattenItems(item[key]));
}
});
flat.push(flatItem);
});
// sort by level before returning
return flat.sort(function(i1, i2) {
return parseInt(i1.level) - parseInt(i2.level);
});
}
这里是 fiddle 使用您的样本数据,检查控制台。
普通JavaScript
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
var r = [];
function flatten(a) {
if (a.length == 0) return;
var o = {};
o.id = a[0].id;
o.level = a[0].level;
o.text = a[0].text;
o.type = a[0].type
o.items = a[0].items == null ? null : []
r.push(o);
if (Array.isArray(a[0].items)) {
flatten(a[0].items);
}
a.shift();
flatten(a);
}
flatten(data);
document.write('<pre>' + JSON.stringify(r, 0, 2) + '</pre>');
有点ES6的味道
function flatten(xs) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
if (x.items) {
acc = acc.concat(flatten(x.items));
x.items = [];
}
return acc;
}, []);
}
使用 reduce
和 recursion
function flatten(data){
return data.reduce(function(result,next){
result.push(next);
if(next.items){
result = result.concat(flatten(next.items));
next.items = [];
}
return result;
},[]);
}
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
var result = flatten(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
只有一个线性函数可以完成这项工作。
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }],
flatIron = (a,b) => a.reduce((p,c) => {!!c.items ? (p.push(c), flatIron(c.items,p), c.items = []) : p.push(c); return p},b),
flatArr = flatIron(data,[]);
document.write('<pre>' + JSON.stringify(flatArr, 0, 2) + '</pre>');
从 Lo-Dash 3.0.0 开始,_.flattenDeep(data) 将 return 一个你想要的深度扁平数组。还有一个 _.flatten(data) 函数可以浅层压平。
递归reducer函数的另一种方式
_.reduce(data, function reducer(result, val) {
var items = _.reduce(val.items, reducer, []);
val.items = _.isArray(val.items) ? [] : val.items;
return _.concat(result, val, items);
}, []);
我需要做同样的事情,在解决我的问题时找到了使用 lodash 解决你的问题的方法:
function kids(node) {
return node.items
? [{...node, items: []}, _.map(node.items, kids)]
: {...node, items: null};
}
_.flatMapDeep(data, kids);
修改了 Роман Парадеев 答案,使其更加动态。
function flatten(xs, childSelector) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
let children = childSelector(x);
if (children) {
acc = acc.concat(flatten(children, childSelector));
}
return acc;
}, []);
}
现在 items
不是硬编码的,您可以使用它 flatten(data, x => x.items)
。
使用 _.flatMapDeep
(自 Lodash 4.7 起可用):
var flatten = function(item) {
return [item, _.flatMapDeep(item.items, flatten)];
}
var result = _.flatMapDeep(data, flatten);
此解决方案应该适用于 IE11;使用过滤器、映射和减少。
var item = function(x) {
return {
"id": x.id,
"level": x.level,
"text": x.text,
"type": x.type,
"items": x.items ? [] : null
}
}
var flatten = function(a, b) {
return a.concat(b);
};
var onlyUnique = function(acc, curr) {
if (acc.length == 0) {
acc.push(curr);
} else {
var search = acc.filter(function(x){return x.id===curr.id;})
if (search.length == 0) {
acc.push(curr);
}
}
return acc;
}
var newData = data.map(function(x) {
return x.items.map(function(xx) {
return xx.items.map(function(xxx) {
return [item(x), item(xx), item(xxx)];
}).reduce(flatten, []);
}).reduce(flatten, [])
}).reduce(flatten, []).reduce(onlyUnique, []);;
console.log(JSON.stringify(newData, null, 2))
由于这个较旧的问题已被重新提出,这里是一个现代版本。它不会改变(@Nina Scholz:或破坏!)原始数据,而是 returns 包含新对象的新数组。
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [node, ... flattenItems (items || [])])
const data = [{id: 1, level: "1", text: "Sammy", type: "Item", items: [{id: 11, level: "2", text: "Table", type: "Item", items: [{id: 111, level: "3", text: "Dog", type: "Item", items: null}, {id: 112, level: "3", text: "Cat", type: "Item", items: null}]}, {id: 12, level: "2", text: "Chair", type: "Item", items: [{id: 121, level: "3", text: "Dog", type: "Item", items: null}, {id: 122, level: "3", text: "Cat", type: "Item", items: null}]}]}, {id: 2, level: "1", text: "Sundy", type: "Item", items: [{id: 21, level: "2", text: "MTable", type: "Item", items: [{id: 211, level: "3", text: "MTDog", type: "Item", items: null}, {id: 212, level: "3", text: "MTCat", type: "Item", items: null}]}, {id: 22, level: "2", text: "MChair", type: "Item", items: [{id: 221, level: "3", text: "MCDog", type: "Item", items: null}, {id: 222, level: "3", text: "MCCat", type: "Item", items: null}]}]}, {id: 3, level: "1", text: "Bruce", type: "Folder", items: [{id: 31, level: "2", text: "BTable", type: "Item", items: [{id: 311, level: "3", text: "BTDog", type: "Item", items: null}, {id: 312, level: "3", text: "BTCat", type: "Item", items: null}]}, {id: 32, level: "2", text: "Chair", type: "Item", items: [{id: 321, level: "3", text: "BCDog", type: "Item", items: null}, {id: 322, level: "3", text: "BCCat", type: "Item", items: null}]}]}]
console .log (flattenItems (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
此版本不包含相当无用的 items
属性。添加它会很容易,像这样:
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [
{... node, items: items == null ? null : []},
... flattenItems (items || [])
])
简单的 map 和 reduce :
export function flatten<T>(value: T): T[] {
const out: T[] = [];
if (value.children) {
const children = Object.values(value.children);
out.push(
value,
...children.map(flatten).reduce((acc, curVal) => {
return acc.concat(curVal);
}),
);
} else {
out.push(value);
}
return out;
}