将递归数组对象转换为平面数组对象
Convert recursive array object to flat array object
我正在寻找一种方法将这个递归对象数组转换为平面对象数组,以便更容易使用。
[
{
"name": "bill",
"car": "jaguar",
"age": 30,
"profiles": [
{
"name": "stacey",
"car": "lambo",
"age": 23,
"profiles": [
{
"name": "martin",
"car": "lexus",
"age": 34,
"profiles": []
}
]
}
]
}
]
这是预期的输出。
[
{
"name": "bill",
"car": "jaguar",
"age": 30,
},{
"name": "stacey",
"car": "lambo",
"age": 23,
},{
"name": "martin",
"car": "lexus",
"age": 34,
}
]
每个 profiles
数组可以有 n
个项目,可能有也可能没有子 profiles
的空数组。请注意,转换后的数组对象在转换后不包含 profiles
。
我愿意使用 underscore
或 lodash
来实现这一目标。
我会使用递归函数并将生成的数组传递给它以避免使用全局变量,如下所示:
var target = [];
var extractElements(source, target) {
//TODO: check if source is array
for (var i=0; i<source.length; i++) {
// create a new element with our data
var newElement = {
name: source[i].name,
car: source[i].car,
age: source[i].age
};
// put it in our flattened array
target.push(newElement);
// check if we need to go deeper and pass our flattened array around
if (source[i].profiles instanceof Array &&
source[i].profiles.length>0)
extractElements(source[i].profiles, target);
}
}
console.log(target) // should list your elements nicely
我还没有测试过,所以用它来寻找灵感,但要小心 :)
(edit1: "var i" 在 for)
让我们称你的原始数据为 o
,将 Array.prototype.reduce 与递归相结合我想出了这个:
o.reduce(function recur(accumulator, curr) {
var keys = Object.keys(curr);
keys.splice(keys.indexOf('profiles'), 1);
accumulator.push(keys.reduce(function (entry, key) {
entry[key] = curr[key];
return entry;
}, {}));
if (curr.profiles.length) {
return accumulator.concat(curr.profiles.reduce(recur, []));
}
return accumulator;
}, []);
你好这个也可以试试...
var out = [];
var i=0;
var extract = function(s, out) {
if(s[0] == null){
i = out.length -1;
return false;
}else {
out.push(s[0]);
}
extract(s[0].profiles, out);
delete out[i--].profiles;
};
extract(a, out); /// here 'a' is the input array and 'out' output
console.log(out);
祝一切顺利...
var _ = require('lodash')
/**
* Flatten a array-object via recursive property
* @see {@link
* @param {Array} arr Array of objects with recursive props
* @param {String} recursiveProperty The string of the recursive property
* @return {Array} Flat array of all recursive properties without recursive property
*/
function arrExtract (arr, recursiveProperty) {
var extracted = []
function _arrExtract (children) {
_.each(children, function (item) {
if (item[recursiveProperty] && item[recursiveProperty].length) _arrExtract(item[recursiveProperty])
extracted.push(_.omit(item, recursiveProperty))
})
}
_arrExtract(arr)
return extracted
}
module.exports = arrExtract
将近三年后,我仍在寻找 one-size 适合的解决方案。在这里,深受@axelduch 的回答影响。
const {isPlainObject, isArray, get, omit, reduce} = require('lodash')
const recursiveFlatten = (tree, headProp, parentIdProp, parentRefProp, parent = {}) => {
tree = isArray(tree) ? tree : [tree]
return reduce(tree, (acq, current) => {
const currentWithoutHead = omit(current, [headProp])
if (parentIdProp && parentRefProp) currentWithoutHead[parentRefProp] = parent[parentIdProp] || null
acq = [...acq, currentWithoutHead]
const next = get(current, headProp)
if (isPlainObject(next) || isArray(next)) {
parent = currentWithoutHead
acq = [...acq, ...recursiveFlatten(next, headProp, parentIdProp, parentRefProp, parent)]
}
return acq
}, [])
}
这是一个简单的例子:
const example = recursiveFlatten({
name: 'bill',
love: true,
lovers: [{
name: 'jil',
love: false,
lovers: [{
name: 'diana',
love: false,
lovers: false
}, {
name: 'einstein',
love: false,
lovers: {
name: 'carl sagan',
love: false,
lovers: false
}
}]
}]
}, 'lovers')
[ { name: 'bill', love: true },
{ name: 'jil', love: false },
{ name: 'diana', love: false },
{ name: 'einstein', love: false },
{ name: 'carl sagan', love: false } ]
这是一个通过 parentRef
.
添加 parentId
prop 的例子
const example = recursiveFlatten({
name: 'bill',
love: true,
lovers: [{
name: 'jil',
love: false,
lovers: [{
name: 'diana',
love: false,
lovers: false
}, {
name: 'einstein',
love: false,
lovers: {
name: 'carl sagan',
love: false,
lovers: false
}
}]
}]
}, 'lovers', 'name', 'parentName')
[ { name: 'bill', love: true, parentName: null },
{ name: 'jil', love: false, parentName: 'bill' },
{ name: 'diana', love: false, parentName: 'jil' },
{ name: 'einstein', love: false, parentName: 'jil' },
{ name: 'carl sagan', love: false, parentName: 'einstein' } ]
const _ = require('lodash')
const arrayFromObject = (currentObject, currentArray) => {
const {profiles, ...rest} = currentObject
if (!_.isEmpty(currentObject.profiles)) {
return arrayFromObject(currentObject.profiles!, [...currentArray, rest])
}
return [...currentArray, rest]
}
const flatArray = arrayFromObject(myRecursiveObject, [])
这里有一个相当简单的技术可以解决最初定义的问题。
const recursiveFlatten = (tree) =>
tree .length == 0
? []
: tree .flatMap (({profiles = [], ... rest}) => [{... rest}, ... recursiveFlatten (profiles)])
const tree = [{name: "bill", car: "jaguar", age: 30, profiles: [{name: "stacey", car: "lambo", age: 23, profiles: [{name: "martin", car: "lexus", age: 34, profiles: []}]}]}, {name: "denise", car: "pinto", age: 28}]
console .log (
recursiveFlatten (tree)
)
这会对名称 "profiles" 进行硬编码并将其删除,从而在生成的副本中保持其余属性完好无损。
您自己的回答提出了更为复杂的要求。这个版本通过几个可选参数来处理这些问题,就像你的答案一样,尽管它的调用方式在这里发生了变化,如果需要的话可以很容易地改变:
const recursiveFlatten = (headProp, parentIdProp, parentRefProp, parent = {}) => (tree) =>
tree .length == 0
? []
: tree .flatMap (({[headProp]: children = [], ... rest}) => [
{
... rest,
... (parentIdProp && parentRefProp ? {[parentRefProp]: parent[parentIdProp] || null} : {})
},
... recursiveFlatten (headProp, parentIdProp, parentRefProp, rest) (children)
])
const tree = [{name: "bill", car: "jaguar", age: 30, profiles: [{name: "stacey", car: "lambo", age: 23, profiles: [{name: "martin", car: "lexus", age: 34, profiles: []}]}]}, {name: "denise", car: "pinto", age: 28}]
console .log (recursiveFlatten ('profiles') (tree))
console .log (recursiveFlatten ('profiles', 'name', 'parentName') (tree))
不过,在我自己的代码库中,我不会对此 API 感到兴奋。取决于传递多少参数的不同行为增加了不必要的复杂性。我可能会将它们埋在 API 下,例如
const recursiveFlatten = (parentIdProp, parentRefProp) => (headProp) => (tree) => ...
然后我们可以创建我们需要的函数,比如使用
const flattenProfiles = recursiveFlatten (null, null) ('profiles')
和
const flattenAndExpand = recuriveFlatten ('name', 'parentName') ('profiles')
替换上面console .log ()
语句中的两个调用。
我正在寻找一种方法将这个递归对象数组转换为平面对象数组,以便更容易使用。
[
{
"name": "bill",
"car": "jaguar",
"age": 30,
"profiles": [
{
"name": "stacey",
"car": "lambo",
"age": 23,
"profiles": [
{
"name": "martin",
"car": "lexus",
"age": 34,
"profiles": []
}
]
}
]
}
]
这是预期的输出。
[
{
"name": "bill",
"car": "jaguar",
"age": 30,
},{
"name": "stacey",
"car": "lambo",
"age": 23,
},{
"name": "martin",
"car": "lexus",
"age": 34,
}
]
每个 profiles
数组可以有 n
个项目,可能有也可能没有子 profiles
的空数组。请注意,转换后的数组对象在转换后不包含 profiles
。
我愿意使用 underscore
或 lodash
来实现这一目标。
我会使用递归函数并将生成的数组传递给它以避免使用全局变量,如下所示:
var target = [];
var extractElements(source, target) {
//TODO: check if source is array
for (var i=0; i<source.length; i++) {
// create a new element with our data
var newElement = {
name: source[i].name,
car: source[i].car,
age: source[i].age
};
// put it in our flattened array
target.push(newElement);
// check if we need to go deeper and pass our flattened array around
if (source[i].profiles instanceof Array &&
source[i].profiles.length>0)
extractElements(source[i].profiles, target);
}
}
console.log(target) // should list your elements nicely
我还没有测试过,所以用它来寻找灵感,但要小心 :)
(edit1: "var i" 在 for)
让我们称你的原始数据为 o
,将 Array.prototype.reduce 与递归相结合我想出了这个:
o.reduce(function recur(accumulator, curr) {
var keys = Object.keys(curr);
keys.splice(keys.indexOf('profiles'), 1);
accumulator.push(keys.reduce(function (entry, key) {
entry[key] = curr[key];
return entry;
}, {}));
if (curr.profiles.length) {
return accumulator.concat(curr.profiles.reduce(recur, []));
}
return accumulator;
}, []);
你好这个也可以试试...
var out = [];
var i=0;
var extract = function(s, out) {
if(s[0] == null){
i = out.length -1;
return false;
}else {
out.push(s[0]);
}
extract(s[0].profiles, out);
delete out[i--].profiles;
};
extract(a, out); /// here 'a' is the input array and 'out' output
console.log(out);
祝一切顺利...
var _ = require('lodash')
/**
* Flatten a array-object via recursive property
* @see {@link
* @param {Array} arr Array of objects with recursive props
* @param {String} recursiveProperty The string of the recursive property
* @return {Array} Flat array of all recursive properties without recursive property
*/
function arrExtract (arr, recursiveProperty) {
var extracted = []
function _arrExtract (children) {
_.each(children, function (item) {
if (item[recursiveProperty] && item[recursiveProperty].length) _arrExtract(item[recursiveProperty])
extracted.push(_.omit(item, recursiveProperty))
})
}
_arrExtract(arr)
return extracted
}
module.exports = arrExtract
将近三年后,我仍在寻找 one-size 适合的解决方案。在这里,深受@axelduch 的回答影响。
const {isPlainObject, isArray, get, omit, reduce} = require('lodash')
const recursiveFlatten = (tree, headProp, parentIdProp, parentRefProp, parent = {}) => {
tree = isArray(tree) ? tree : [tree]
return reduce(tree, (acq, current) => {
const currentWithoutHead = omit(current, [headProp])
if (parentIdProp && parentRefProp) currentWithoutHead[parentRefProp] = parent[parentIdProp] || null
acq = [...acq, currentWithoutHead]
const next = get(current, headProp)
if (isPlainObject(next) || isArray(next)) {
parent = currentWithoutHead
acq = [...acq, ...recursiveFlatten(next, headProp, parentIdProp, parentRefProp, parent)]
}
return acq
}, [])
}
这是一个简单的例子:
const example = recursiveFlatten({
name: 'bill',
love: true,
lovers: [{
name: 'jil',
love: false,
lovers: [{
name: 'diana',
love: false,
lovers: false
}, {
name: 'einstein',
love: false,
lovers: {
name: 'carl sagan',
love: false,
lovers: false
}
}]
}]
}, 'lovers')
[ { name: 'bill', love: true },
{ name: 'jil', love: false },
{ name: 'diana', love: false },
{ name: 'einstein', love: false },
{ name: 'carl sagan', love: false } ]
这是一个通过 parentRef
.
parentId
prop 的例子
const example = recursiveFlatten({
name: 'bill',
love: true,
lovers: [{
name: 'jil',
love: false,
lovers: [{
name: 'diana',
love: false,
lovers: false
}, {
name: 'einstein',
love: false,
lovers: {
name: 'carl sagan',
love: false,
lovers: false
}
}]
}]
}, 'lovers', 'name', 'parentName')
[ { name: 'bill', love: true, parentName: null },
{ name: 'jil', love: false, parentName: 'bill' },
{ name: 'diana', love: false, parentName: 'jil' },
{ name: 'einstein', love: false, parentName: 'jil' },
{ name: 'carl sagan', love: false, parentName: 'einstein' } ]
const _ = require('lodash')
const arrayFromObject = (currentObject, currentArray) => {
const {profiles, ...rest} = currentObject
if (!_.isEmpty(currentObject.profiles)) {
return arrayFromObject(currentObject.profiles!, [...currentArray, rest])
}
return [...currentArray, rest]
}
const flatArray = arrayFromObject(myRecursiveObject, [])
这里有一个相当简单的技术可以解决最初定义的问题。
const recursiveFlatten = (tree) =>
tree .length == 0
? []
: tree .flatMap (({profiles = [], ... rest}) => [{... rest}, ... recursiveFlatten (profiles)])
const tree = [{name: "bill", car: "jaguar", age: 30, profiles: [{name: "stacey", car: "lambo", age: 23, profiles: [{name: "martin", car: "lexus", age: 34, profiles: []}]}]}, {name: "denise", car: "pinto", age: 28}]
console .log (
recursiveFlatten (tree)
)
这会对名称 "profiles" 进行硬编码并将其删除,从而在生成的副本中保持其余属性完好无损。
您自己的回答提出了更为复杂的要求。这个版本通过几个可选参数来处理这些问题,就像你的答案一样,尽管它的调用方式在这里发生了变化,如果需要的话可以很容易地改变:
const recursiveFlatten = (headProp, parentIdProp, parentRefProp, parent = {}) => (tree) =>
tree .length == 0
? []
: tree .flatMap (({[headProp]: children = [], ... rest}) => [
{
... rest,
... (parentIdProp && parentRefProp ? {[parentRefProp]: parent[parentIdProp] || null} : {})
},
... recursiveFlatten (headProp, parentIdProp, parentRefProp, rest) (children)
])
const tree = [{name: "bill", car: "jaguar", age: 30, profiles: [{name: "stacey", car: "lambo", age: 23, profiles: [{name: "martin", car: "lexus", age: 34, profiles: []}]}]}, {name: "denise", car: "pinto", age: 28}]
console .log (recursiveFlatten ('profiles') (tree))
console .log (recursiveFlatten ('profiles', 'name', 'parentName') (tree))
不过,在我自己的代码库中,我不会对此 API 感到兴奋。取决于传递多少参数的不同行为增加了不必要的复杂性。我可能会将它们埋在 API 下,例如
const recursiveFlatten = (parentIdProp, parentRefProp) => (headProp) => (tree) => ...
然后我们可以创建我们需要的函数,比如使用
const flattenProfiles = recursiveFlatten (null, null) ('profiles')
和
const flattenAndExpand = recuriveFlatten ('name', 'parentName') ('profiles')
替换上面console .log ()
语句中的两个调用。