问题
I searched for this but couldn't find a satisfactory answer so I'm posting my own answer here.
Basically I wanted a function that:
- takes an object as its argument
- recursively removes properties whose values are
null
,undefined
,[]
,{}
or''
- retains
0
andfalse
values - returns a new object with those properties removed
- preferably in a functional style without mutations
回答1:
Here's what I came up with. (Thanks to Nina for providing a sample ;)
const is_obj = x => x !== null && typeof x === 'object';
const is_arr = x => Array.isArray(x);
const nullish = x =>
( typeof x !== 'number'
&& typeof x !== 'boolean'
&& typeof x !== 'function'
)
&&
( x === undefined
|| x === null
|| x === ''
|| Object.values(x).reduce((res, x) =>
res && nullish(x), true)
);
const clean = x =>
[x]
.map(x => Object.entries(x))
.map(x => x.map(([k, v]) =>
is_arr(v) ? [ k
, v.map(vv => is_obj(vv) ? clean(vv) : vv)
]
: is_obj(v) ? [ k
, clean(v)
]
: [ k
, v
]))
.map(x => x.filter(([k, v]) => !nullish(v)))
.map(x => Object.fromEntries(x))
.pop();
console.log(clean(data));
<script>
var data = {
emptyArray: [],
arrayWithNullish: [null, {}, [], undefined],
null: null,
undefined: undefined,
emptyString: '',
zero: 0,
false: false,
true: true,
emptyObject: {},
objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
nestedObject: {
nestedObject: {
null: null,
one: 1,
emptyObject: {}
},
nestedEmptyArray: [[], [[]]]
}
};
</script>
回答2:
You could separate the three types of data in
- array,
- object
- primitive value
and get the wanted subset by reducing the complex data types and checking the primitive values.
const
isNullish = x => [
v => v === '',
v => v === null,
v => v === undefined,
v => v && typeof v === 'object' && !Object.keys(v).length
].some(f => f(x)),
getArray = array => {
var temp = array.reduce((r, v) => {
v = getNotNullish(v);
if (v !== undefined) r.push(v);
return r;
}, []);
return temp.length ? temp : undefined;
},
getObject = object => {
var hasValues = false,
temp = Object.entries(object).reduce((r, [k, v]) => {
v = getNotNullish(v);
if (v !== undefined) {
r[k] = v;
hasValues = true;
}
return r;
}, {});
return hasValues ? temp : undefined;
},
getNotNullish = value => {
if (Array.isArray(value)) return getArray(value);
if (value && typeof value === 'object') return getObject(value);
return isNullish(value) ? undefined : value;
};
var data = {
emptyArray: [],
arrayWithNullish: [null, {}, [], undefined],
null: null,
undefined: undefined,
emptyString: '',
zero: 0,
false: false,
true: true,
emptyObject: {},
objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
nestedObject: {
nestedObject: {
null: null,
one: 1,
emptyObject: {}
},
nestedEmptyArray: [[], [[]]]
}
};
console.log(getNotNullish(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
回答3:
I am using the data sample from one of the answers here.
var data = {
emptyArray: [],
arrayWithNullish: [null, {}, [], undefined],
null: null,
undefined: undefined,
emptyString: '',
zero: 0,
false: false,
true: true,
emptyObject: {},
objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
nestedObject: {
nestedObject: {
null: null,
one: 1,
emptyObject: {}
},
nestedEmptyArray: [[], [[]], 6]
}
};
function clean(data){
return (function inner(data, output){
if (!isObject(data)){
return data;
}
Object.keys(data).forEach(function(key){
if(isObject(data[key]) && !Array.isArray(data[key])){
var result = clean(data[key], output);
updateVal(output, key, result);
}else if(Array.isArray(data[key])){
var new_arr = [];
data[key].forEach(function(a_item){
var a_result = clean(a_item, output);
if (!isFalsy(a_result)){
new_arr.push(a_item);
}
});
updateVal(output, key, new_arr);
}else{
updateVal(output, key, data[key]);
}
});
return output;
})(data, {});
}
function updateVal(output,key, val){
if(!isFalsy(val)){
output[key] = val;
}
}
function isObject(data){
return typeof data === "object" && data !== null;
}
function isFalsy(val){
return ['', undefined, null].indexOf(val) !== -1 ?
true:
(()=>{
return typeof(val) === "object" && Object.keys(val).length === 0 ? true: false;
})();
}
console.log(clean(data));
回答4:
Here's what I came up with (thanks to Nina for the test data):
function stripNullsFromObject(obj) {
function stripNullsFromArray(arr) {
return arr.reduce((acc, cur) => {
if (typeof cur === 'object' && !Array.isArray(cur) && cur !== null) {
const nestedObj = stripNullsFromObject(cur);
if (Object.entries(nestedObj).length > 0) {
return acc.concat(nestedObj);
}
}
if (Array.isArray(cur)) {
const nestedArr = stripNullsFromArray(cur);
if (nestedArr.length > 0) {
return acc.concat(nestedArr);
}
}
if (typeof cur !== 'object' && (!!cur || cur === 0 || cur === false)) {
return acc.concat(cur);
}
return acc;
}, []);
}
return Object.entries(obj).reduce((acc, [key, val]) => {
if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
const nestedObj = stripNullsFromObject(val);
if (Object.entries(nestedObj).length > 0) {
return {
...acc,
[key]: nestedObj,
};
}
}
if (Array.isArray(val)) {
const nestedArr = stripNullsFromArray(val);
if (nestedArr.length > 0) {
return {
...acc,
[key]: nestedArr,
};
}
}
if (typeof val !== 'object' && (!!val || val === 0 || val === false)) {
return {
...acc,
[key]: val,
};
}
return acc;
}, {});
}
const data = {
emptyArray: [],
arrayWithNullish: [null, {}, [], undefined],
null: null,
undefined: undefined,
emptyString: '',
zero: 0,
false: false,
true: true,
emptyObject: {},
objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
nestedObject: {
nestedObject: {
null: null,
one: 1,
emptyObject: {}
},
nestedEmptyArray: [[], [[]]]
}
};
console.log(stripNullsFromObject(data))
I'm curious if anyone else has an alternative method, or suggestions for how this could be improved.
来源:https://stackoverflow.com/questions/59413342/recursively-remove-nullish-values-from-a-javascript-object