Элегантный способ сплющить объект - PullRequest
1 голос
/ 05 июля 2019

Я сталкиваюсь с тривиальной проблемой сглаживания простого объекта с вложенным внутри него.

Попытка решения 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
}

Ответы [ 7 ]

1 голос
/ 05 июля 2019

Просто для того, чтобы поделиться другим подходом (возможно, достаточно элегантно), вот решение, полагающееся на генераторы функций для рекурсивного выравнивания объекта.

Поскольку оно опирается на генераторы функций, вы можете в конечном итоге построить объект динамическии пропустить нежелательные ключи из-за того, что результат является итеративным.

Приведенный ниже пример намеренно сделан немного более сложным для обработки массивов и 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);
1 голос
/ 05 июля 2019

Это было бы проще с методом 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());

Лично я предпочитаю этот последний подход, так какон не изменяет оригинал или любой объект.

1 голос
/ 05 июля 2019

Предполагая, что вы хотите иметь общее решение, а не то, которое специально адаптировано для вашего 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));
0 голосов
/ 05 июля 2019

Если вам нужно сплющить объект и ничего больше, и слои объектов потенциально могут быть n +, а типы свойств могут быть разными, тогда я предлагаю использовать простые:

JSON.stringify(object);
0 голосов
/ 05 июля 2019

Попробуйте это (это сгладит любой объект, содержащийся в любом объекте), перебирая атрибуты объекта и определяя, является ли атрибут другим объектом для выравнивания и добавляется к «корневому»:

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));
0 голосов
/ 05 июля 2019

Я обычно использую 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}
0 голосов
/ 05 июля 2019

Просто объедините и удалите каждое дочернее свойство, которое является экземпляром Object.

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);
...