Как удалить все атрибуты, которые есть undefinedили nullв объекте JavaScript?
(Вопрос похож на этот для массивов)
Как удалить все атрибуты, которые есть undefinedили nullв объекте JavaScript?
(Вопрос похож на этот для массивов)
Ответы:
Вы можете перебрать объект:
var test = {
test1 : null,
test2 : 'somestring',
test3 : 3,
}
function clean(obj) {
for (var propName in obj) {
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
}
clean(test);
Если вас беспокоит, что удаление этого свойства не запускает цепочку типов объекта, вы также можете:
function clean(obj) {
var propNames = Object.getOwnPropertyNames(obj);
for (var i = 0; i < propNames.length; i++) {
var propName = propNames[i];
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
}
Несколько замечаний о нуле против неопределенных:
test.test1 === null; // true
test.test1 == null; // true
test.notaprop === null; // false
test.notaprop == null; // true
test.notaprop === undefined; // true
test.notaprop == undefined; // true
Используя некоторые ES6 / ES2015 :
1) Простой однострочник для удаления элементов в строке без присваивания:
Object.keys(myObj).forEach((key) => (myObj[key] == null) && delete myObj[key]);
2) Этот пример был удален ...
3) Первый пример, написанный как функция:
const removeEmpty = obj => {
Object.keys(obj).forEach(key => obj[key] == null && delete obj[key]);
};
4) Эта функция также использует рекурсию для удаления элементов из вложенных объектов:
const removeEmpty = obj => {
Object.keys(obj).forEach(key => {
if (obj[key] && typeof obj[key] === "object") removeEmpty(obj[key]); // recurse
else if (obj[key] == null) delete obj[key]; // delete
});
};
4b) Это похоже на 4), но вместо непосредственного изменения исходного объекта он возвращает новый объект.
const removeEmpty = obj => {
const newObj = {};
Object.keys(obj).forEach(key => {
if (obj[key] && typeof obj[key] === "object") {
newObj[key] = removeEmpty(obj[key]); // recurse
} else if (obj[key] != null) {
newObj[key] = obj[key]; // copy value
}
});
return newObj;
};
5) Функциональный подход к 4b), основанный на ответе @ MichaelJ.Zoidl с использованием filter()и reduce(). Этот также возвращает новый объект:
const removeEmpty = obj =>
Object.keys(obj)
.filter(k => obj[k] != null) // Remove undef. and null.
.reduce(
(newObj, k) =>
typeof obj[k] === "object"
? { ...newObj, [k]: removeEmpty(obj[k]) } // Recurse.
: { ...newObj, [k]: obj[k] }, // Copy value.
{}
);
6) То же, что 4), но с ES7 / 2016 Object.entries() .
const removeEmpty = (obj) =>
Object.entries(obj).forEach(([key, val]) => {
if (val && typeof val === 'object') removeEmpty(val)
else if (val == null) delete obj[key]
})
5b) Еще одна функциональная
версия, которая использует рекурсию и возвращает новый объект с ES2019 Object.fromEntries() :
const removeEmpty = obj =>
Object.fromEntries(
Object.entries(obj)
.filter(([k, v]) => v != null)
.map(([k, v]) => (typeof v === "object" ? [k, removeEmpty(v)] : [k, v]))
);
7) То же, что 4), но в простой ES5 :
function removeEmpty(obj) {
Object.keys(obj).forEach(function(key) {
if (obj[key] && typeof obj[key] === 'object') removeEmpty(obj[key])
else if (obj[key] == null) delete obj[key]
});
};
keysиз object, так oи kочевидны. Но я думаю, это вопрос вкуса.
Object.keys(myObj).forEach(function (key) {(myObj[key] == null) && delete myObj[key]});
Object.entries(myObj).reduce((acc, [key, val]) => { if (val) acc[key] = val; return acc; }, {})
Если вы используете lodash или underscore.js, вот простое решение:
var obj = {name: 'John', age: null};
var compacted = _.pickBy(obj);
Это будет работать только с lodash 4, pre lodash 4 или underscore.js, use _.pick(obj, _.identity);
_.omit(obj, _.isUndefined)лучше.
_.isUndefinedне пропускает нули, используйте _.omitBy(obj, _.isNil)для пропуска обоих undefinedиnull
Самый короткий лайнер для ES6 +
Фильтр всего falsy значения ( "", 0, false, null, undefined)
Object.entries(obj).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})
Фильтр nullи undefinedзначения:
Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {})
Фильтр ТОЛЬКО null
Object.entries(obj).reduce((a,[k,v]) => (v === null ? a : (a[k]=v, a)), {})
Фильтр ТОЛЬКО undefined
Object.entries(obj).reduce((a,[k,v]) => (v === undefined ? a : (a[k]=v, a)), {})
Рекурсивные решения: фильтры nullиundefined
Для объектов:
const cleanEmpty = obj => Object.entries(obj)
.map(([k,v])=>[k,v && typeof v === "object" ? cleanEmpty(v) : v])
.reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
Для объектов и массивов:
const cleanEmpty = obj => {
if (Array.isArray(obj)) {
return obj
.map(v => (v && typeof v === 'object') ? cleanEmpty(v) : v)
.filter(v => !(v == null));
} else {
return Object.entries(obj)
.map(([k, v]) => [k, v && typeof v === 'object' ? cleanEmpty(v) : v])
.reduce((a, [k, v]) => (v == null ? a : (a[k]=v, a)), {});
}
}
v == nullвы будете проверять undefinedи null.
cleanEmptyrecursve решение возвращает пустой объект {}для объектов Даты
Если кому-то нужна рекурсивная версия ответа Оуэна (и Эрика), вот он:
/**
* Delete all null (or undefined) properties from an object.
* Set 'recurse' to true if you also want to delete properties in nested objects.
*/
function delete_null_properties(test, recurse) {
for (var i in test) {
if (test[i] === null) {
delete test[i];
} else if (recurse && typeof test[i] === 'object') {
delete_null_properties(test[i], recurse);
}
}
}
hasOwnPropertyиспользуетif(test.hasOwnProperty(i)) { ... }
JSON.stringify удаляет неопределенные ключи.
removeUndefined = function(json){
return JSON.parse(JSON.stringify(json))
}
nullобращаться undefinedс функцией замены, для получения дополнительной информации обратитесь к этому ответу: stackoverflow.com/questions/286141/…
nullзначения. Попробуй: let a = { b: 1, c: 0, d: false, e: null, f: undefined, g: [], h: {} }а потом console.log(removeUndefined(a)). Вопрос был о undefinedи nullценности.
Вы можете использовать комбинацию JSON.stringify, его параметр заменителя, и JSON.parseпревратить его обратно в объект. Использование этого метода также означает, что замена выполняется для всех вложенных ключей во вложенных объектах.
Пример объекта
var exampleObject = {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3],
object: {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
arrayOfObjects: [
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
}
]
};
Функция заменителя
function replaceUndefinedOrNull(key, value) {
if (value === null || value === undefined) {
return undefined;
}
return value;
}
Очистить объект
exampleObject = JSON.stringify(exampleObject, replaceUndefinedOrNull);
exampleObject = JSON.parse(exampleObject);
Использование ramda # pickBy вы удалите все null, undefinedи falseзначения:
const obj = {a:1, b: undefined, c: null, d: 1}
R.pickBy(R.identity, obj)
Как отметил @manroe, для сохранения falseзначений используйте isNil():
const obj = {a:1, b: undefined, c: null, d: 1, e: false}
R.pickBy(v => !R.isNil(v), obj)
(v) => !R.isNil(v)вероятно, лучший выбор для вопроса ОП, учитывая, что falseили другие ложные значения также будут отклоненыR.identity
Функциональный и неизменный подход, без .filterи без создания большего количества объектов, чем необходимо
Object.keys(obj).reduce((acc, key) => (obj[key] === undefined ? acc : {...acc, [key]: obj[key]}), {})
obj[key] === undefinedнаobj[key] === undefined || obj[key] === null
const omitFalsy = obj => Object.keys(obj).reduce((acc, key) => ({ ...acc, ...(obj[key] && { [key]: obj[key] }) }), {});
Вы можете сделать рекурсивное удаление в одной строке, используя аргумент заменителя json.stringify
const removeEmptyValues = obj => (
JSON.parse(JSON.stringify(obj, (k,v) => v ?? undefined))
)
Использование:
removeEmptyValues({a:{x:1,y:null,z:undefined}}) // Returns {a:{x:1}}
Как упоминалось в комментарии Эммануэля, этот метод работал, только если ваша структура данных содержит только типы данных, которые могут быть переведены в формат JSON (строки, числа, списки и т. Д.).
(Этот ответ был обновлен , чтобы использовать новый оператор Nullish коалесцирующего в зависимости от потребностей поддержки браузера вы можете использовать эту функцию вместо:. (k,v) => v!=null ? v : undefined)
NaNв nullкоторые не удаляются.
Вы можете сделать короче с !условием
var r = {a: null, b: undefined, c:1};
for(var k in r)
if(!r[k]) delete r[k];
Помните при использовании: как @semicolor объявляет в комментариях: это также приведет к удалению свойств, если значением является пустая строка, false или ноль
[null, undefined].includes(r[k])вместо !r[k].
Короче чистое решение ES6, конвертируйте его в массив, используйте функцию фильтра и конвертируйте его обратно в объект. Также было бы легко сделать функцию ...
Btw. при этом .length > 0я проверяю, есть ли пустая строка / массив, поэтому он удалит пустые ключи.
const MY_OBJECT = { f: 'te', a: [] }
Object.keys(MY_OBJECT)
.filter(f => !!MY_OBJECT[f] && MY_OBJECT[f].length > 0)
.reduce((r, i) => { r[i] = MY_OBJECT[i]; return r; }, {});
nullи undefinedбыло бы проще просто использовать MY_OBJECT[f] != null. Ваше текущее решение удаляет все, кроме непустых строк / списков и выдает ошибку, когда значенияnull
filter, будет более читабельным.
omitделает loadash , вам нужно проверить, существует ли obj перед вызовом Object.keys:const omit = (obj, filter) => obj && Object.keys(obj).filter(key => !filter(obj[key])).reduce((acc,key) => {acc[key] = obj[key]; return acc}, {});
Если вы хотите 4 линии чистого решения ES7:
const clean = e => e instanceof Object ? Object.entries(e).reduce((o, [k, v]) => {
if (typeof v === 'boolean' || v) o[k] = clean(v);
return o;
}, e instanceof Array ? [] : {}) : e;
Или, если вы предпочитаете более читаемую версию:
function filterEmpty(obj, [key, val]) {
if (typeof val === 'boolean' || val) {
obj[key] = clean(val)
};
return obj;
}
function clean(entry) {
if (entry instanceof Object) {
const type = entry instanceof Array ? [] : {};
const entries = Object.entries(entry);
return entries.reduce(filterEmpty, type);
}
return entry;
}
Это сохранит логические значения и также очистит массивы. Он также сохраняет исходный объект, возвращая очищенную копию.
У меня такой же сценарий в моем проекте, и я достиг этого, используя следующий метод.
Он работает со всеми типами данных, немногие из упомянутых выше не работают с датой и пустыми массивами.
removeEmptyKeysFromObject.js
removeEmptyKeysFromObject(obj) {
Object.keys(obj).forEach(key => {
if (Object.prototype.toString.call(obj[key]) === '[object Date]' && (obj[key].toString().length === 0 || obj[key].toString() === 'Invalid Date')) {
delete obj[key];
} else if (obj[key] && typeof obj[key] === 'object') {
this.removeEmptyKeysFromObject(obj[key]);
} else if (obj[key] == null || obj[key] === '') {
delete obj[key];
}
if (obj[key]
&& typeof obj[key] === 'object'
&& Object.keys(obj[key]).length === 0
&& Object.prototype.toString.call(obj[key]) !== '[object Date]') {
delete obj[key];
}
});
return obj;
}
передать любой объект этой функции removeEmptyKeysFromObject ()
Для глубокого поиска я использовал следующий код, может быть, он будет полезен всем, кто смотрит на этот вопрос (его нельзя использовать для циклических зависимостей):
function removeEmptyValues(obj) {
for (var propName in obj) {
if (!obj[propName] || obj[propName].length === 0) {
delete obj[propName];
} else if (typeof obj[propName] === 'object') {
removeEmptyValues(obj[propName]);
}
}
return obj;
}
Если вы не хотите изменять на месте, но возвращаете клон с удаленным значением null / undefined, вы можете использовать функцию сокращения ES6.
// Helper to remove undefined or null properties from an object
function removeEmpty(obj) {
// Protect against null/undefined object passed in
return Object.keys(obj || {}).reduce((x, k) => {
// Check for null or undefined
if (obj[k] != null) {
x[k] = obj[k];
}
return x;
}, {});
}
Чтобы piggypack на ответ Бена о том , как решить эту проблему с помощью lodash - х _.pickBy, вы можете решить эту проблему в библиотеке сестры: Underscore.js «s _.pick.
var obj = {name: 'John', age: null};
var compacted = _.pick(obj, function(value) {
return value !== null && value !== undefined;
});
Смотрите пример JSFiddle
Если кому-то нужно удалить undefinedзначения из объекта с использованием глубокого поиска, lodashто вот код, который я использую. Это довольно просто изменить, чтобы удалить все пустые значения ( null/ undefined).
function omitUndefinedDeep(obj) {
return _.reduce(obj, function(result, value, key) {
if (_.isObject(value)) {
result[key] = omitUndefinedDeep(value);
}
else if (!_.isUndefined(value)) {
result[key] = value;
}
return result;
}, {});
}
С Лодашем:
_.omitBy({a: 1, b: null}, (v) => !v)
Если вы используете eslint и хотите избежать срабатывания правила no-param-reassign, вы можете использовать Object.assign в сочетании с .reduce и вычисленным именем свойства для довольно элегантного решения ES6:
const queryParams = { a: 'a', b: 'b', c: 'c', d: undefined, e: null, f: '', g: 0 };
const cleanParams = Object.keys(queryParams)
.filter(key => queryParams[key] != null)
.reduce((acc, key) => Object.assign(acc, { [key]: queryParams[key] }), {});
// { a: 'a', b: 'b', c: 'c', f: '', g: 0 }
Вот функциональный способ удаления nullsобъекта с помощью ES6 без изменения объекта с использованием только reduce:
const stripNulls = (obj) => {
return Object.keys(obj).reduce((acc, current) => {
if (obj[current] !== null) {
return { ...acc, [current]: obj[current] }
}
return acc
}, {})
}
stripNullsфункции он использует ссылку вне области действия функции аккумулятора; и это также смешивает проблемы, фильтруя в функции аккумулятора. 😝 (например Object.entries(o).filter(([k,v]) => v !== null).reduce((o, [k, v]) => {o[k] = v; return o;}, {});) Да, он будет дважды проходить по фильтрованным элементам, но реализованная потеря производительности там незначительна.
Вы также можете использовать ...синтаксис распространения, используя forEachчто-то вроде этого:
let obj = { a: 1, b: "b", c: undefined, d: null };
let cleanObj = {};
Object.keys(obj).forEach(val => {
const newVal = obj[val];
cleanObj = newVal ? { ...cleanObj, [val]: newVal } : cleanObj;
});
console.info(cleanObj);
// General cleanObj function
const cleanObj = (valsToRemoveArr, obj) => {
Object.keys(obj).forEach( (key) =>
if (valsToRemoveArr.includes(obj[key])){
delete obj[key]
}
})
}
cleanObj([undefined, null], obj)
const getObjWithoutVals = (dontReturnValsArr, obj) => {
const cleanObj = {}
Object.entries(obj).forEach( ([key, val]) => {
if(!dontReturnValsArr.includes(val)){
cleanObj[key]= val
}
})
return cleanObj
}
//To get a new object without `null` or `undefined` run:
const nonEmptyObj = getObjWithoutVals([undefined, null], obj)
Мы можем использовать JSON.stringify и JSON.parse для удаления пустых атрибутов из объекта.
jsObject = JSON.parse(JSON.stringify(jsObject), (key, value) => {
if (value == null || value == '' || value == [] || value == {})
return undefined;
return value;
});
{} != {}и [] != []), но в противном случае подход действителен
Вот полная рекурсивная функция (изначально основанная на функции @chickens), которая будет:
defaults=[undefined, null, '', NaN]const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
ИСПОЛЬЗОВАНИЕ:
// based off the recursive cleanEmpty function by @chickens.
// This one can also handle Date objects correctly
// and has a defaults list for values you want stripped.
const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
// testing
console.log('testing: undefined \n', cleanEmpty(undefined))
console.log('testing: null \n',cleanEmpty(null))
console.log('testing: NaN \n',cleanEmpty(NaN))
console.log('testing: empty string \n',cleanEmpty(''))
console.log('testing: empty array \n',cleanEmpty([]))
console.log('testing: date object \n',cleanEmpty(new Date(1589339052 * 1000)))
console.log('testing: nested empty arr \n',cleanEmpty({ 1: { 2 :null, 3: [] }}))
console.log('testing: comprehensive obj \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: new Date(),
c: ''
}]
},
g: NaN,
h: null
}))
console.log('testing: different defaults \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: '',
c: new Date()
}]
},
g: [0, 1, 2, 3, 4],
h: '',
}, [undefined, null]))
Если вы предпочитаете чистый / функциональный подход
const stripUndef = obj =>
Object.keys(obj)
.reduce((p, c) => ({ ...p, ...(x[c] === undefined ? { } : { [c]: x[c] })}), {});