一种扁平化对象的优雅方式
An elegant way to flatten an object
我面临着用嵌套对象压平简单对象的琐碎问题。
尝试从 SO 解决方案,但它抛出错误:
const newWeather = Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}({id: 1}))
// also tried these ones:
console.log(Object.keys(weatherDetails).reduce((a, b, c) => {
return Object.assign(a, {
a: b
})
}, {}));
// another one
let newWeather = Object.assign({}, (function() {
var obj = {}
for (var i = 0; i < Object.keys(weatherDetails).length; i++) {
console.log(i, Object.keys(weatherDetails))
obj[Object.keys(weatherDetails)] = weatherDetails[Object.keys(weatherDetails)]
}
return obj
})())
这是我需要展平的对象,所以我们需要转动它:
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
}
进入这个:
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
PM1: 1,
PM10: 2,
PM25: 3
}
假设您想要一个通用的解决方案,而不是 custom-tailored 您的带有静态键的 pollution
示例的解决方案,这里有一个快速实现的方法:
您只需遍历对象的 属性 键。如果 属性 是一个对象(我们称之为子对象),您需要将子对象的属性复制到主对象。
const obj = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
function flatten(object) {
for (const key in object) {
if (!object.hasOwnProperty(key)) {
continue;
}
if (typeof object[key] === 'object' && !Array.isArray(object[key]) && object[key] != null) {
const childObject = object[key];
delete object[key];
object = {...object, ...childObject};
}
}
return object;
}
console.log(flatten(obj));
只需合并并删除每个 属性 对象实例的子对象。
let obj =
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3,
pollution: 4
}
};
function flatten(obj)
{
obj = Object.assign({}, obj);
for (let i in obj)
if (obj[i] instanceof Object)
{
obj = Object.assign(obj, obj[i]);
// Prevent deletion of property i/"pollution", if it was not replaced by one of the child object's properties
if (obj[i] === obj[i][i])
delete obj[i];
}
return obj;
}
let obj_flattened = flatten(obj);
console.log(obj_flattened);
使用 Object.entries() 方法会更容易
您遍历对象键和值,删除所有以对象为值的条目,并将该值之外的条目分配给对象。
let a = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
}
Object.entries(a).map(([key, value]) => {
if(value && typeof value === 'object') {
delete a[key]; // Delete entry
Object.assign(a, value); // Add values from entry to object
}
});
console.log(a)
一个班轮:
Object.entries(a).map(([key, value]) => value && typeof value === 'object' && delete a[key] && Object.assign(a, value));
还有一个不可变的函数方法:
Object.fromEntries(Object.entries(a).map(([key, value]) =>
value && typeof value === 'object' ?
Object.entries(value) : [[key, value]]
).flat());
我个人更喜欢最后一种方法,因为它不会改变原始对象或任何对象。
我通常使用 Lodash 进行此类转换。
有了它,做起来就很简单了。
查看以下代码示例:
const data = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
let flat = _.merge(data, data.pollution);
delete flat.pollution;
console.log(flat); // returns {"temperature":null,"humidity":null,"pressure":null,"windspeed":null,"PM1":1,"PM10":2,"PM25":3}
只是为了分享一种不同的方法(可能足够优雅),这是一个依赖函数生成器递归地展平对象的解决方案。
因为它依赖于函数生成器,您最终可以动态构建对象并跳过不需要的键,因为结果是可迭代的。
为了处理数组和 null
值,下面的示例有意变得稍微复杂一些,尽管在原始问题中不是必需的。
const original = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
arrayKey: [1,2,3,'star!'],
fnKey: function(i) {
return i * 3;
},
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
// Flattens an object.
function* flattenObject(obj, flattenArray = false) {
// Loop each key -> value pair entry in the provided object.
for (const [key, value] of Object.entries(obj)) {
// If the target value is an object and it's not null (because typeof null is 'object'), procede.
if (typeof(value) === 'object' && value !== null) {
// if the targeted value is an array and arrays should be flattened, flatten the array.
if (Array.isArray(value) && flattenArray) yield* flattenObject(value);
// Otherwise, if the value is not an array, flatten it (it must be an object-like or object type).
else if (!Array.isArray(value)) yield* flattenObject(value);
// otherwise, just yield the key->value pair.
else yield [key, value];
}
// otherwise, the value must be something which is not an object, hence, just yield it.
else yield [key, value];
}
}
// usage: assign to a new object all the flattened properties, using the spread operator (...) to assign the values progressively.
const res = Object.fromEntries(flattenObject(original));
console.log(res);
// sample usage by flattening arrays as well.
const res_flattened_arrays = Object.fromEntries(flattenObject(original, true));
console.log(res_flattened_arrays);
// custom object building by skipping a desired key
const resWithoutTemperature = {};
for (const [key, value] of flattenObject(original)) {
if (key !== 'temperature') resWithoutTemperature[key] = value;
}
console.log(resWithoutTemperature);
试试这个(它将展平任何对象中包含的任何对象)遍历对象属性并确定一个属性是否是另一个要展平的对象并添加到 "root" 一个:
var o = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3,
newobject:{
a:1,
b:2,
c: {
x:3,
y:4,
z:5
}
}
}
}
function flatten(obj){
let retObj = {};
let objConst = {}.constructor;
for (el in obj){
if(obj[el] !== null && obj[el].constructor === objConst){
retObj = Object.assign({}, retObj, flatten(obj[el]));
} else {
retObj[el] = obj[el];
}
}
return retObj;
}
console.log(flatten(o));
我面临着用嵌套对象压平简单对象的琐碎问题。
尝试从 SO 解决方案,但它抛出错误:
const newWeather = Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}({id: 1}))
// also tried these ones:
console.log(Object.keys(weatherDetails).reduce((a, b, c) => {
return Object.assign(a, {
a: b
})
}, {}));
// another one
let newWeather = Object.assign({}, (function() {
var obj = {}
for (var i = 0; i < Object.keys(weatherDetails).length; i++) {
console.log(i, Object.keys(weatherDetails))
obj[Object.keys(weatherDetails)] = weatherDetails[Object.keys(weatherDetails)]
}
return obj
})())
这是我需要展平的对象,所以我们需要转动它:
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
}
进入这个:
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
PM1: 1,
PM10: 2,
PM25: 3
}
假设您想要一个通用的解决方案,而不是 custom-tailored 您的带有静态键的 pollution
示例的解决方案,这里有一个快速实现的方法:
您只需遍历对象的 属性 键。如果 属性 是一个对象(我们称之为子对象),您需要将子对象的属性复制到主对象。
const obj = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
function flatten(object) {
for (const key in object) {
if (!object.hasOwnProperty(key)) {
continue;
}
if (typeof object[key] === 'object' && !Array.isArray(object[key]) && object[key] != null) {
const childObject = object[key];
delete object[key];
object = {...object, ...childObject};
}
}
return object;
}
console.log(flatten(obj));
只需合并并删除每个 属性 对象实例的子对象。
let obj =
{
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3,
pollution: 4
}
};
function flatten(obj)
{
obj = Object.assign({}, obj);
for (let i in obj)
if (obj[i] instanceof Object)
{
obj = Object.assign(obj, obj[i]);
// Prevent deletion of property i/"pollution", if it was not replaced by one of the child object's properties
if (obj[i] === obj[i][i])
delete obj[i];
}
return obj;
}
let obj_flattened = flatten(obj);
console.log(obj_flattened);
使用 Object.entries() 方法会更容易
您遍历对象键和值,删除所有以对象为值的条目,并将该值之外的条目分配给对象。
let a = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
}
Object.entries(a).map(([key, value]) => {
if(value && typeof value === 'object') {
delete a[key]; // Delete entry
Object.assign(a, value); // Add values from entry to object
}
});
console.log(a)
一个班轮:
Object.entries(a).map(([key, value]) => value && typeof value === 'object' && delete a[key] && Object.assign(a, value));
还有一个不可变的函数方法:
Object.fromEntries(Object.entries(a).map(([key, value]) =>
value && typeof value === 'object' ?
Object.entries(value) : [[key, value]]
).flat());
我个人更喜欢最后一种方法,因为它不会改变原始对象或任何对象。
我通常使用 Lodash 进行此类转换。 有了它,做起来就很简单了。
查看以下代码示例:
const data = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
let flat = _.merge(data, data.pollution);
delete flat.pollution;
console.log(flat); // returns {"temperature":null,"humidity":null,"pressure":null,"windspeed":null,"PM1":1,"PM10":2,"PM25":3}
只是为了分享一种不同的方法(可能足够优雅),这是一个依赖函数生成器递归地展平对象的解决方案。
因为它依赖于函数生成器,您最终可以动态构建对象并跳过不需要的键,因为结果是可迭代的。
为了处理数组和 null
值,下面的示例有意变得稍微复杂一些,尽管在原始问题中不是必需的。
const original = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
arrayKey: [1,2,3,'star!'],
fnKey: function(i) {
return i * 3;
},
pollution: {
PM1: 1,
PM10: 2,
PM25: 3
}
};
// Flattens an object.
function* flattenObject(obj, flattenArray = false) {
// Loop each key -> value pair entry in the provided object.
for (const [key, value] of Object.entries(obj)) {
// If the target value is an object and it's not null (because typeof null is 'object'), procede.
if (typeof(value) === 'object' && value !== null) {
// if the targeted value is an array and arrays should be flattened, flatten the array.
if (Array.isArray(value) && flattenArray) yield* flattenObject(value);
// Otherwise, if the value is not an array, flatten it (it must be an object-like or object type).
else if (!Array.isArray(value)) yield* flattenObject(value);
// otherwise, just yield the key->value pair.
else yield [key, value];
}
// otherwise, the value must be something which is not an object, hence, just yield it.
else yield [key, value];
}
}
// usage: assign to a new object all the flattened properties, using the spread operator (...) to assign the values progressively.
const res = Object.fromEntries(flattenObject(original));
console.log(res);
// sample usage by flattening arrays as well.
const res_flattened_arrays = Object.fromEntries(flattenObject(original, true));
console.log(res_flattened_arrays);
// custom object building by skipping a desired key
const resWithoutTemperature = {};
for (const [key, value] of flattenObject(original)) {
if (key !== 'temperature') resWithoutTemperature[key] = value;
}
console.log(resWithoutTemperature);
试试这个(它将展平任何对象中包含的任何对象)遍历对象属性并确定一个属性是否是另一个要展平的对象并添加到 "root" 一个:
var o = {
temperature: null,
humidity: null,
pressure: null,
windspeed: null,
pollution: {
PM1: 1,
PM10: 2,
PM25: 3,
newobject:{
a:1,
b:2,
c: {
x:3,
y:4,
z:5
}
}
}
}
function flatten(obj){
let retObj = {};
let objConst = {}.constructor;
for (el in obj){
if(obj[el] !== null && obj[el].constructor === objConst){
retObj = Object.assign({}, retObj, flatten(obj[el]));
} else {
retObj[el] = obj[el];
}
}
return retObj;
}
console.log(flatten(o));