更新:我以错误的方式描述了这个问题,并完全重写了这个描述,以及有效的代码,但是它既丑陋又有限。
让我们假装有一个对象
const input = {
a: 1,
b: '2',
c: {
d: true,
e: '4'
},
f: [{
g: 5,
h: {
i: '6'
}
}, {
g: 7,
h: {
i: '8'
}
}]
}
我正在寻找的是嵌套数组的所有可能排列的集合,对象的键被展平并与“。”连接,如
[{
a: 1,
b: '2',
'c.d': true,
'c.e': '4',
'f.g': 5,
'f.h.i': '6'
}, {
a: 1,
b: '2',
'c.d': true,
'c.e': '4',
'f.g': 7,
'f.h.i': '8'
}]
请注意,没有任何键具有非原始值,例如'f.h'
指向对象。
所以,我先做的是收集所有密钥,然后人为地将#
符号添加到指向数组项的每个键,因此#
表示“该数组中的每个索引“:
function columns(data, prefix = '') {
if (_.isArray(data)) {
return columns(_.first(data), `${prefix}.#`);
} else if (_.isObject(data)) {
return _.filter(_.flatMap(_.keys(data), key => {
return _.concat(
!_.isObject(_.result(data, key)) ? `${prefix}.${key}` : null,
columns(data[key], `${prefix}.${key}`)
);
}));
} else {
return null;
}
}
console.log(columns(input)); // -> [".a", ".b", ".c.d", ".c.e", ".f.#.g", ".f.#.h.i"]
现在,我挥舞着lodash。领先的“。”在key中对于lodash不是问题,所以我只是保持原样。使用lodash,我将对象压缩成具有奇怪键的一级对象:
function flattenKeys(original, keys) {
return _.mapValues(_.groupBy(_.map(keys, key => ({
key,
value: _.result(original, key)
})), 'key'), e => _.result(e, '0.value'));
}
console.log(flattenKeys(input, columns(input))) // -> {".a":1,".b":"2",".c.d":true,".c.e":"4"}
现在我(通过一种非常错误的方式)运行原始对象的每个类似数组的属性并生成一个对象数组,为.f.#.h.i
设置值为.f.0.h.i
的第一个元素等等:
function unfold(original, keys, iterables) {
if (!_.isArray(iterables)) {
return unfold(original, keys, _.uniq(_.map(_.filter(keys, key => /#/i.test(key)), key => _.replace(key, /\.\#.*/, ''))));
} else if (_.isEmpty(iterables)) {
return [];
} else {
const first = _.first(iterables);
const rest = _.tail(iterables);
const values = _.result(original, first);
const flatKeys = _.mapKeys(_.filter(keys, key => _.includes(key, first)));
const updated = _.map(values, (v, i) => ({
...flattenKeys(original, keys),
..._.mapValues(flatKeys, k => _.result(original, _.replace(k, /\#/, i)))
}));
return _.concat(updated, unfold(original, keys, rest));
}
}
console.log(unfold(input, columns(input))) // -> [{".a":1,".b":"2",".c.d":true,".c.e":"4",".f.#.g":5,".f.#.h.i":"6"},{".a":1,".b":"2",".c.d":true,".c.e":"4",".f.#.g":7,".f.#.h.i":"8"}]
所以最后,我只需要清理密钥,事实上,在我的情况下,这不是必需的。
问题是,除了代码的丑陋之外,我如何才能使其与原始对象中可能的多个类数组属性一起工作?
现在,我明白,这个问题更适合CodeReview StackExchange,所以如果有人将它转移到那里,我就可以了。
答案 0 :(得分:1)
根据您更新的结构,以下递归函数可以解决这个问题:
function unfold(input) {
function flatten(obj) {
var result = {},
f,
key,
keyf;
for(key in obj) {
if(obj[key] instanceof Array) {
obj[key].forEach(function(k) {
f = flatten(k);
for(keyf in f) {
result[key+'.'+keyf] = f[keyf];
}
output.push(JSON.parse(JSON.stringify(result))); //poor man's clone object
});
} else if(obj[key] instanceof Object) {
f = flatten(obj[key]);
for(keyf in f) {
result[key+'.'+keyf] = f[keyf];
}
} else {
result[key] = obj[key];
}
}
return result;
} //flatten
var output = [];
flatten(input);
return output;
} //unfold
<强>段:强>
function unfold(input) {
function flatten(obj) {
var result = {},
f,
key,
keyf;
for(key in obj) {
if(obj[key] instanceof Array) {
obj[key].forEach(function(k) {
f = flatten(k);
for(keyf in f) {
result[key+'.'+keyf] = f[keyf];
}
output.push(JSON.parse(JSON.stringify(result))); //poor man's clone object
});
} else if(obj[key] instanceof Object) {
f = flatten(obj[key]);
for(keyf in f) {
result[key+'.'+keyf] = f[keyf];
}
} else {
result[key] = obj[key];
}
}
return result;
} //flatten
var output = [];
flatten(input);
return output;
} //unfold
const input = {
a: 1,
b: '2',
c: {
d: true,
e: '4'
},
f: [{
g: 5,
h: {
i: '6'
}
}, {
g: 7,
h: {
i: '8'
}
}]
};
document.body.innerHTML+= '<pre>' + JSON.stringify(unfold(input), null, 2) + '</pre>';
我将留下原始答案,该答案适用于您的原始结构:
var o = {a: [{b: 1, c: 2}], d: [{e: 4, f: 5}]},
keys = Object.keys(o),
result = [];
keys.forEach(function(i, idx1) {
keys.forEach(function(j, idx2) {
if(idx2 > idx1) { //avoid duplicates
for(var k in o[i][0]) {
for(var l in o[j][0]) {
result.push({
[i + '.' + k]: o[i][0][k],
[j + '.' + l]: o[j][0][l]
});
}
}
}
});
});
console.log(JSON.stringify(result));