Один вкладыш для сглаживания вложенного объекта
Мне нужно сгладить вложенный объект. Нужен один лайнер. Не уверен, какой правильный термин для этого процесса. Я могу использовать чистый Javascript или библиотеки, мне особенно нравится подчеркивание.
У меня есть...
{
a:2,
b: {
c:3
}
}
И я хочу...
{
a:2,
c:3
}
Я пробовал...
var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "fred")
alert(JSON.stringify(resultObj));
Что работает, но мне также нужно, чтобы это работало...
var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "john")
alert(JSON.stringify(resultObj));
Ответы
Ответ 1
Ну вот:
Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}(yourObject))
Сводка: рекурсивно создайте массив объектов с одним свойством, а затем объедините их все с Object.assign
.
Это использует функции ES6, включая Object.assign
или оператор распространения, но их должно быть достаточно просто переписать, чтобы не требовать их.
Для тех, кто не заботится о однострочном безумии и предпочел бы иметь возможность действительно читать его (в зависимости от вашего определения читаемости):
Object.assign(
{},
...function _flatten(o) {
return [].concat(...Object.keys(o)
.map(k =>
typeof o[k] === 'object' ?
_flatten(o[k]) :
({[k]: o[k]})
)
);
}(yourObject)
)
Ответ 2
Упрощенный читаемый пример, без зависимостей
/**
* Flatten a multidimensional object
*
* For example:
* flattenObject({ a: 1, b: { c: 2 } })
* Returns:
* { a: 1, c: 2}
*/
export const flattenObject = (obj) => {
const flattened = {}
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(flattened, flattenObject(obj[key]))
} else {
flattened[key] = obj[key]
}
})
return flattened
}
Рабочий пример: https://jsfiddle.net/webbertakken/jn613d8p/2/
Ответ 3
Это не совсем один лайнер, но вот решение, которое от ES6 ничего не требует. Он использует метод extend
подчеркивания, который может быть заменен для jQuery.
function flatten(obj) {
var flattenedObj = {};
Object.keys(obj).forEach(function(key){
if (typeof obj[key] === 'object') {
$.extend(flattenedObj, flatten(obj[key]));
} else {
flattenedObj[key] = obj[key];
}
});
return flattenedObj;
}
Ответ 4
Вот ванильные решения, которые работают с массивами, примитивами, регулярными выражениями, функциями, любым количеством уровней вложенных объектов и практически всем остальным, что я могу на них бросить. Первый перезаписывает значения свойств так, как вы ожидаете от Object.assign
.
((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: Object.assign({}, ...function leaves(o) {
return [].concat.apply([], Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(key => v.hasOwnProperty(key))
|| Array.isArray(v))
? {[k]: v}
: leaves(v)
);
})
);
}(o))
})(o)
Вторая накапливает значения в массив.
((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: (function () {
return Object.values((function leaves(o) {
return [].concat.apply([], !o ? [] : Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(k => v.hasOwnProperty(k))
|| (Array.isArray(v) && !v.some(el => typeof el === 'object')))
? {[k]: v}
: leaves(v)
);
})
);
}(o))).reduce((acc, cur) => {
return ((key) => {
acc[key] = !acc[key] ? [cur[key]]
: new Array(...new Set(acc[key].concat([cur[key]])))
})(Object.keys(cur)[0]) ? acc : acc
}, {})
})(o);
})(o)
Также, пожалуйста, не включайте подобный код в производство, так как его очень сложно отлаживать.
function leaves1(o) {
return ((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: Object.assign({}, ...function leaves(o) {
return [].concat.apply([], Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(key => v.hasOwnProperty(key))
|| Array.isArray(v))
? {[k]: v}
: leaves(v)
);
})
);
}(o))
})(o);
}
function leaves2(o) {
return ((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: (function () {
return Object.values((function leaves(o) {
return [].concat.apply([], !o ? [] : Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(k => v.hasOwnProperty(k))
|| (Array.isArray(v) && !v.some(el => typeof el === 'object')))
? {[k]: v}
: leaves(v)
);
})
);
}(o))).reduce((acc, cur) => {
return ((key) => {
acc[key] = !acc[key] ? [cur[key]]
: new Array(...new Set(acc[key].concat([cur[key]])))
})(Object.keys(cur)[0]) ? acc : acc
}, {})
})(o);
})(o);
}
const obj = {
l1k0: 'foo',
l1k1: {
l2k0: 'bar',
l2k1: {
l3k0: {},
l3k1: null
},
l2k2: undefined
},
l1k2: 0,
l2k3: {
l3k2: true,
l3k3: {
l4k0: [1,2,3],
l4k1: [4,5,'six', {7: 'eight'}],
l4k2: {
null: 'test',
[{}]: 'obj',
[Array.prototype.map]: Array.prototype.map,
l5k3: ((o) => (typeof o === 'object'))(this.obj),
}
}
},
l1k4: '',
l1k5: new RegExp(/[\s\t]+/g),
l1k6: function(o) { return o.reduce((a,b) => a+b)},
false: [],
}
const objs = [null, undefined, {}, [], ['non', 'empty'], 42, /[\s\t]+/g, obj];
objs.forEach(o => {
console.log(leaves1(o));
});
objs.forEach(o => {
console.log(leaves2(o));
});
Ответ 5
Это функция, которую я получил в своих общих библиотеках именно для этой цели. Я считаю, что я получил это из аналогичного вопроса stackoverflow, но не могу вспомнить, какой (отредактируйте: Самый быстрый способ сгладить/не сгладить вложенные объекты JSON - спасибо Yoshi!)
function flatten(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
Это можно затем вызвать следующим образом:
var myJSON = '{a:2, b:{c:3}}';
var myFlattenedJSON = flatten(myJSON);
Вы также можете добавить эту функцию в стандартный класс строки Javascript следующим образом:
String.prototype.flattenJSON = function() {
var data = this;
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
С помощью которого вы можете сделать следующее:
var flattenedJSON = '{a:2, b:{c:3}}'.flattenJSON();
Ответ 6
function flatten(obj: any) {
return Object.keys(obj).reduce((acc, current) => {
const key = '${current}';
const currentValue = obj[current];
if (Array.isArray(currentValue) || Object(currentValue) === currentValue) {
Object.assign(acc, flatten(currentValue));
} else {
acc[key] = currentValue;
}
return acc;
}, {});
};
let obj = {
a:2,
b: {
c:3
}
}
console.log(flatten(obj))
Демо https://stackblitz.com/edit/typescript-flatten-json
Ответ 7
Мне нравится этот код, потому что его немного легче понять.
const data = {
a: "a",
b: {
c: "c",
d: {
e: "e",
f: [
"g",
{
i: "i",
j: {},
k: []
}
]
}
}
};
function flatten(data: any, flatKey = "", response = {}) {
for (const [key, value] of Object.entries(data)) {
const newFlatKey = '${flatKey}${key}';
if (typeof value === "object" && value !== null && Object.keys(value).length > 0) {
flatten(value, '${newFlatKey}.', response);
} else {
response[newFlatKey] = value;
}
}
return response;
};
console.log(flatten(data));