Loop through complex react state to remove all empty strings - javascript

I have a complex react state that is structured like this:
{
a: [
{
a: ['b','',]
}
],
b: {
a: ['b',''],
b: {c:'e',f:''}
},
c: 'd',
e: '',
f: ['g',''],
date: new Date()
}
As you can see, could be in the object a lot of empty strings. Before I submit it to the backend, I would like to iterate over the state object and remove all empty values. So I first need to deep clone the state not to mess with my applications state which can be done by:
clone = JSON.parse(JSON.stringify(state))
but how do I iterate over the clone and remove all empty strings?

You can pass a replacer function into your JSON.stringify() method. Returning undefined from the replacement function will remove the key-value pair, so you can return undefined when you encounter an empty string. Likewise, when you encounter an array, you can filter it to only contain values which are non-empty strings.
See example below:
const state = {
a: [{
a: ['b', '', ]
}],
b: {
a: ['b', ''],
b: {
c: 'e',
f: ''
}
},
c: 'd',
e: '',
f: ['g', ''],
h: {},
date: new Date()
};
const clone = JSON.parse(JSON.stringify(state, (key, value) => {
if (typeof value === 'string' && value === "" || Object(value) === value && Object.keys(value).length === 0) {
return undefined;
} else if(Array.isArray(value)) {
return value.filter(val => val !== "");
}
return value;
}));
console.log(clone);

You can also create a new object with a dedicated function that recursively removes the empty strings. Something like this:
const removeEmpties = (obj) =>
Array .isArray (obj)
? obj .filter (x => x !== '') .map (removeEmpties)
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj)
.filter (([k, v]) => v !== '')
.map (([k, v]) => [k, removeEmpties (v)]
))
: obj
const input = {a: [{a: ['b','',]}], b: {a: ['b',''], b: {c:'e',f:''}}, c: 'd', e: '', f: ['g',''], date: new Date()}
console .log (removeEmpties (input))
.as-console-wrapper {min-height: 100% !important; top: 0}
But that could be generalized to a more reusable functionlike this:
const deepFilter = (pred) => (obj) =>
Array .isArray (obj)
? obj .filter (pred) .map (deepFilter (pred))
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj)
.filter (([k, v]) => pred(v))
.map (([k, v]) => [k, deepFilter (pred) (v)]
))
: obj
And then specialized for your case this way:
const removeEmpties = deepFilter (x => x !== '')

Related

parsed javascript object in array by remove null values [duplicate]

How do I remove all attributes which are undefined or null in a JavaScript object?
(Question is similar to this one for Arrays)
ES10/ES2019 examples
A simple one-liner (returning a new object).
let o = Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
Same as above but written as a function.
function removeEmpty(obj) {
return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.fromEntries(
Object.entries(obj)
.filter(([_, v]) => v != null)
.map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v])
);
}
ES6/ES2015 examples
A simple one-liner. Warning: This mutates the given object instead of returning a new one.
Object.keys(obj).forEach((k) => obj[k] == null && delete obj[k]);
A single declaration (not mutating the given object).
let o = Object.keys(obj)
.filter((k) => obj[k] != null)
.reduce((a, k) => ({ ...a, [k]: obj[k] }), {});
Same as above but written as a function.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce(
(acc, [k, v]) => ({ ...acc, [k]: v === Object(v) ? removeEmpty(v) : v }),
{}
);
}
Same as the function above, but written in an imperative (non-functional) style.
function removeEmpty(obj) {
const newObj = {};
Object.entries(obj).forEach(([k, v]) => {
if (v === Object(v)) {
newObj[k] = removeEmpty(v);
} else if (v != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
ES5/ES2009 examples
In the old days things were a lot more verbose.
This is a non recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = obj[k];
return acc;
}, {});
}
This is a non recursive version written in an imperative style.
function removeEmpty(obj) {
const newObj = {};
Object.keys(obj).forEach(function (k) {
if (obj[k] && typeof obj[k] === "object") {
newObj[k] = removeEmpty(obj[k]);
} else if (obj[k] != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
And a recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = typeof obj[k] === "object" ? removeEmpty(obj[k]) : obj[k];
return acc;
}, {});
}
You can loop through the object:
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];
}
}
return obj
}
console.log(test);
console.log(clean(test));
If you're concerned about this property removal not running up object's proptype chain, you can also:
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];
}
}
}
A few notes on null vs undefined:
test.test1 === null; // true
test.test1 == null; // true
test.notaprop === null; // false
test.notaprop == null; // true
test.notaprop === undefined; // true
test.notaprop == undefined; // true
Shortest one liners for ES6+
Filter all falsy values ( "", 0, false, null, undefined )
Object.entries(obj).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})
Filter null and undefined values:
Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {})
Filter ONLY null
Object.entries(obj).reduce((a,[k,v]) => (v === null ? a : (a[k]=v, a)), {})
Filter ONLY undefined
Object.entries(obj).reduce((a,[k,v]) => (v === undefined ? a : (a[k]=v, a)), {})
Recursive Solutions: Filters null and undefined
For Objects:
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)), {});
For Objects and Arrays:
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)), {});
}
}
If you are using lodash or underscore.js, here is a simple solution:
var obj = {name: 'John', age: null};
var compacted = _.pickBy(obj);
This will only work with lodash 4, pre lodash 4 or underscore.js, use _.pick(obj, _.identity);
If somebody needs a recursive version of Owen's (and Eric's) answer, here it is:
/**
* 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);
}
}
}
JSON.stringify removes the undefined keys.
removeUndefined = function(json){
return JSON.parse(JSON.stringify(json))
}
You can use a combination of JSON.stringify, its replacer parameter, and JSON.parse to turn it back into an object. Using this method also means the replacement is done to all nested keys within nested objects.
Example Object
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]
}
]
};
Replacer Function
function replaceUndefinedOrNull(key, value) {
if (value === null || value === undefined) {
return undefined;
}
return value;
}
Clean the Object
exampleObject = JSON.stringify(exampleObject, replaceUndefinedOrNull);
exampleObject = JSON.parse(exampleObject);
CodePen example
Simplest possible Lodash solution to return an object with the null and undefined values filtered out.
_.omitBy(obj, _.isNil)
You can do a recursive removal in one line using json.stringify's replacer argument
const removeEmptyValues = obj => (
JSON.parse(JSON.stringify(obj, (k,v) => v ?? undefined))
)
Usage:
removeEmptyValues({a:{x:1,y:null,z:undefined}}) // Returns {a:{x:1}}
As mentioned in Emmanuel's comment, this technique only worked if your data structure contains only data types that can be put into JSON format (strings, numbers, lists, etc).
(This answer has been updated to use the new Nullish Coalescing operator. depending on browser support needs you may want to use this function instead: (k,v) => v!=null ? v : undefined)
You are probably looking for the delete keyword.
var obj = { };
obj.theProperty = 1;
delete obj.theProperty;
you can do shorter with ! condition
var r = {a: null, b: undefined, c:1};
for(var k in r)
if(!r[k]) delete r[k];
Remember in usage : as #semicolor announce in comments: This would also delete properties if the value is an empty string, false or zero
Remove all the properties with null and undefined
let obj = {
"id": 1,
"firstName": null,
"lastName": null,
"address": undefined,
"role": "customer",
"photo": "fb79fd5d-06c9-4097-8fdc-6cebf73fab26/fc8efe82-2af4-4c81-bde7-8d2f9dd7994a.jpg",
"location": null,
"idNumber": null,
};
let result = Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
console.log(result)
I have same scenario in my project and achieved using following method.
It works with all data types, few mentioned above doesn't work with date and empty arrays .
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;
}
pass any object to this function removeEmptyKeysFromObject()
Using ramda#pickBy you will remove all null, undefined and false values:
const obj = {a:1, b: undefined, c: null, d: 1}
R.pickBy(R.identity, obj)
As #manroe pointed out, to keep false values use isNil():
const obj = {a:1, b: undefined, c: null, d: 1, e: false}
R.pickBy(v => !R.isNil(v), obj)
Shorter ES6 pure solution, convert it to an array, use the filter function and convert it back to an object.
Would also be easy to make a function...
Btw. with this .length > 0 i check if there is an empty string / array, so it will remove empty keys.
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; }, {});
JS BIN https://jsbin.com/kugoyinora/edit?js,console
Functional and immutable approach, without .filter and without creating more objects than needed
Object.keys(obj).reduce((acc, key) => (obj[key] === undefined ? acc : {...acc, [key]: obj[key]}), {})
Instead of delete the property, you can also create a new object with the keys that are not null.
const removeEmpty = (obj) => {
return Object.keys(obj).filter(key => obj[key]).reduce(
(newObj, key) => {
newObj[key] = obj[key]
return newObj
}, {}
)
}
For a deep search I used the following code, maybe it will be useful for anyone looking at this question (it is not usable for cyclic dependencies ) :
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;
}
Here is a comprehensive recursive function (originally based on the one by #chickens) that will:
recursively remove what you tell it to defaults=[undefined, null, '', NaN]
Correctly handle regular objects, arrays and Date objects
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
}
USAGE:
// 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]))
Here's an alternative
Typescript:
function objectDefined <T>(obj: T): T {
const acc: Partial<T> = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc as T;
}
Javascript:
function objectDefined(obj) {
const acc = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc;
}
If you want 4 lines of a pure ES7 solution:
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;
Or if you prefer more readable version:
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;
}
This will preserve boolean values and it will clean arrays too. It also preserves the original object by returning a cleaned copy.
a reduce helper can do the trick (without type checking) -
const cleanObj = Object.entries(objToClean).reduce((acc, [key, value]) => {
if (value) {
acc[key] = value;
}
return acc;
}, {});
If you don't want to mutate in place, but return a clone with the null/undefined removed, you could use the ES6 reduce function.
// 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;
}, {});
}
To piggypack on Ben's answer on how to solve this problem using lodash's _.pickBy, you can also solve this problem in the sister library: Underscore.js's _.pick.
var obj = {name: 'John', age: null};
var compacted = _.pick(obj, function(value) {
return value !== null && value !== undefined;
});
See: JSFiddle Example
You can also use ... spread syntax using forEach something like this:
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);
If someone needs to remove undefined values from an object with deep search using lodash then here is the code that I'm using. It's quite simple to modify it to remove all empty values (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;
}, {});
}
With Lodash:
_.omitBy({a: 1, b: null}, (v) => !v)
If you use eslint and want to avoid tripping the the no-param-reassign rule, you can use Object.assign in conjunction with .reduce and a computed property name for a fairly elegant ES6 solution:
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 }
Here is a functional way to remove nulls from an Object using ES6 without mutating the object using only reduce:
const stripNulls = (obj) => {
return Object.keys(obj).reduce((acc, current) => {
if (obj[current] !== null) {
return { ...acc, [current]: obj[current] }
}
return acc
}, {})
}
Recursively remove null, undefined, empty objects and empty arrays, returning a copy (ES6 version)
export function skipEmpties(dirty) {
let item;
if (Array.isArray(dirty)) {
item = dirty.map(x => skipEmpties(x)).filter(value => value !== undefined);
return item.length ? item : undefined;
} else if (dirty && typeof dirty === 'object') {
item = {};
Object.keys(dirty).forEach(key => {
const value = skipEmpties(dirty[key]);
if (value !== undefined) {
item[key] = value;
}
});
return Object.keys(item).length ? item : undefined;
} else {
return dirty === null ? undefined : dirty;
}
}

Remove all empty strings, empty objects, and empty arrays from JS object

this is my object:
{
"name":"fff",
"onlineConsultation":false,
"image":"",
"primaryLocation":{
"locationName":"ggg",
"street":"",
},
"billingAndInsurance":[
],
"categories":[
""
],
"concernsTreated":[
""
],
"education":[
{
"nameOfInstitution":"ffff",
"description":"fff",
}
],
"experience":[
{
"from":"",
"current":"",
}
],
}
What is the algorithm to recursively remove all empty objects, and empty arrays from this?
this is my code:
function rm(obj) {
for (let k in obj) {
const s = JSON.stringify(obj[k]);
if (s === '""' || s === "[]" || s === "{}") {
delete obj[k];
}
if (Array.isArray(obj[k])) {
obj[k] = obj[k].filter((x) => {
const s = JSON.stringify(obj[x]);
return s !== '""' && s !== "[]" && s !== "{}";
});
obj[k] = obj[k].map(x=>{
return rm(x)
})
}
}
return obj
}
I'v tried multiple algorithms, but none worked. the one above should work with a little more completeness. But I'v exhausted all my resources to make it work
One nice thing about keeping around helpful functions is that you can often solve for your new requirements pretty simply. Using some library functions I've written over the years, I was able to write this version:
const removeEmpties = (input) =>
pathEntries (input)
.filter (([k, v]) => v !== '')
.reduce ((a, [k, v]) => assocPath (k, v, a), {})
This uses two function I had around, pathEntries and assocPath, and I'll give their implementations below. It returns the following when given the input you supplied:
{
name: "fff",
onlineConsultation: false,
primaryLocation: {
locationName: "ggg"
},
education: [
{
nameOfInstitution: "ffff",
description: "fff"
}
]
}
This removes empty string, arrays with no values (after the empty strings are removed) and objects with no non-empty values.
We begin by calling pathEntries (which I've used in other answers here, including a fairly recent one.) This collects paths to all the leaf nodes in the input object, along with the values at those leaves. The paths are stored as arrays of strings (for objects) or numbers (for arrays.) And they are embedded in an array with the value. So after that step we get something like
[
[["name"], "fff"],
[["onlineConsultation"], false],
[["image"], ""],
[["primaryLocation", "locationName"], "ggg"],
[["primaryLocation", "street"], ""],
[["categories", 0], ""],
[["concernsTreated", 0], ""],
[["education", 0, "nameOfInstitution"], "ffff"],
[["education", 0, "description"],"fff"],
[["experience", 0, "from"], ""],
[["experience", 0, "current"], ""]
]
This should looks something like the result of Object.entries for an object, except that the key is not a property name but an entire path.
Next we filter to remove any with an empty string value, yielding:
[
[["name"], "fff"],
[["onlineConsultation"], false],
[["primaryLocation", "locationName"], "ggg"],
[["education", 0, "nameOfInstitution"], "ffff"],
[["education", 0, "description"],"fff"],
]
Then by reducing calls to assocPath (another function I've used quite a few times, including in a very interesting question) over this list and an empty object, we hydrate a complete object with just these leaf nodes at their correct paths, and we get the answer we're seeking. assocPath is an extension of another function assoc, which immutably associates a property name with a value in an object. While it's not as simple as this, due to handling of arrays as well as objects, you can think of assoc like (name, val, obj) => ({...obj, [name]: val}) assocPath does something similar for object paths instead of property names.
The point is that I wrote only one new function for this, and otherwise used things I had around.
Often I would prefer to write a recursive function for this, and I did so recently for a similar problem. But that wasn't easily extensible to this issue, where, if I understand correctly, we want to exclude an empty string in an array, and then, if that array itself is now empty, to also exclude it. This technique makes that straightforward. In the implementation below we'll see that pathEntries depends upon a recursive function, and assocPath is itself recursive, so I guess there's still recursion going on!
I also should note that assocPath and the path function used in pathEntries are inspired by Ramda (disclaimer: I'm one of it's authors.) I built my first pass at this in the Ramda REPL and only after it was working did I port it to vanilla JS, using the versions of dependencies I've created for those previous questions. So even though there are a number of functions in the snippet below, it was quite quick to write.
const path = (ps = []) => (obj = {}) =>
ps .reduce ((o, p) => (o || {}) [p], obj)
const assoc = (prop, val, obj) =>
Number .isInteger (prop) && Array .isArray (obj)
? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
: {...obj, [prop]: val}
const assocPath = ([p = undefined, ...ps], val, obj) =>
p == undefined
? obj
: ps.length == 0
? assoc(p, val, obj)
: assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)
const getPaths = (obj) =>
Object (obj) === obj
? Object .entries (obj) .flatMap (
([k, v]) => getPaths (v) .map (p => [Array.isArray(obj) ? Number(k) : k, ... p])
)
: [[]]
const pathEntries = (obj) =>
getPaths (obj) .map (p => [p, path (p) (obj)])
const removeEmpties = (input) =>
pathEntries (input)
.filter (([k, v]) => v !== '')
.reduce ((a, [k, v]) => assocPath (k, v, a), {})
const input = {name: "fff", onlineConsultation: false, image: "", primaryLocation: {locationName: "ggg", street:""}, billingAndInsurance: [], categories: [""], concernsTreated: [""], education: [{nameOfInstitution: "ffff", description: "fff"}], experience: [{from: "", current:""}]}
console .log(removeEmpties (input))
At some point, I may choose to go a little further. I see a hydrate function looking to be pulled out:
const hydrate = (entries) =>
entries .reduce ((a, [k, v]) => assocPath2(k, v, a), {})
const removeEmpties = (input) =>
hydrate (pathEntries (input) .filter (([k, v]) => v !== ''))
And I can also see this being written more Ramda-style like this:
const hydrate = reduce ((a, [k, v]) => assocPath(k, v, a), {})
const removeEmpties = pipe (pathEntries, filter(valueNotEmpty), hydrate)
with an appropriate version of valuesNotEmpty.
But all that is for another day.
It's an interesting problem. I think it can be solved elegantly if we write a generic map and filter function that works on both Arrays and Objects -
const map = (t, f) =>
isArray(t)
? t.map(f)
: isObject(t)
? Object.fromEntries(Object.entries(t).map(([k, v]) => [k, f(v, k)]))
: t
const filter = (t, f) =>
isArray(t)
? t.filter(f)
: isObject(t)
? Object.fromEntries(Object.entries(t).filter(([k, v]) => f(v, k)))
: t
We can write your removeEmpties program easily now -
if the input, t, is an object, recursively map over it and keep the non-empty values
(inductive) t is not an object. If t is a non-empty value, return t
(inductive) t is not an object and t is an empty value. Return the empty sentinel
const empty =
Symbol()
const removeEmpties = (t = {}) =>
isObject(t)
? filter(map(t, removeEmpties), nonEmpty) // 1
: nonEmpty(t)
? t // 2
: empty // 3
Now we have to define what it means to be nonEmpty -
const nonEmpty = t =>
isArray(t)
? t.length > 0
: isObject(t)
? Object.keys(t).length > 0
: isString(t)
? t.length > 0
: t !== empty // <- all other t are OK, except for sentinel
To this point we have use is* functions to do dynamic type-checking. We will define those now -
const isArray = t => Array.isArray(t)
const isObject = t => Object(t) === t
const isString = t => String(t) === t
const isNumber = t => Number(t) === t
const isMyType = t => // As many types as you want
Finally we can compute the result of your input -
const input =
{name:"fff",zero:0,onlineConsultation:false,image:"",primaryLocation:{locationName:"ggg",street:""},billingAndInsurance:[],categories:[""],concernsTreated:[""],education:[{nameOfInstitution:"ffff",description:"fff"}],experience:[{from:"",current:""}]}
const result =
removeEmpties(input)
console.log(JSON.stringify(result, null, 2))
{
"name": "fff",
"zero": 0,
"onlineConsultation": false,
"primaryLocation": {
"locationName": "ggg"
},
"education": [
{
"nameOfInstitution": "ffff",
"description": "fff"
}
]
}
Expand the program below to verify the result in your browser -
const map = (t, f) =>
isArray(t)
? t.map(f)
: isObject(t)
? Object.fromEntries(Object.entries(t).map(([k, v]) => [k, f(v, k)]))
: t
const filter = (t, f) =>
isArray(t)
? t.filter(f)
: isObject(t)
? Object.fromEntries(Object.entries(t).filter(([k, v]) => f(v, k)))
: t
const empty =
Symbol()
const removeEmpties = (t = {}) =>
isObject(t)
? filter(map(t, removeEmpties), nonEmpty)
: nonEmpty(t)
? t
: empty
const isArray = t => Array.isArray(t)
const isObject = t => Object(t) === t
const isString = t => String(t) === t
const nonEmpty = t =>
isArray(t)
? t.length > 0
: isObject(t)
? Object.keys(t).length > 0
: isString(t)
? t.length > 0
: t !== empty
const input =
{name:"fff",zero:0,onlineConsultation:false,image:"",primaryLocation:{locationName:"ggg",street:""},billingAndInsurance:[],categories:[""],concernsTreated:[""],education:[{nameOfInstitution:"ffff",description:"fff"}],experience:[{from:"",current:""}]}
const result =
removeEmpties(input)
console.log(JSON.stringify(result, null, 2))
function removeEmpty(obj){
if(obj.__proto__.constructor.name==="Array"){
obj = obj.filter(e=>e.length)
return obj.map((ele,i)=>{
if(obj.__proto__.constructor.name==="Object")return removeEmpty(ele) /* calling the same function*/
else return ele
})
}
if(obj.__proto__.constructor.name==="Object")for(let key in obj){
switch(obj[key].__proto__.constructor.name){
case "String":
if(obj[key].length===0)delete obj[key]
break;
case "Array":
obj[key] = removeEmpty(obj[key]) /* calling the same function*/
if(! obj[key].length)delete obj[key]
break;
case "Object":
obj[key] = removeEmpty(obj[key]) /* calling the same function*/
break;
}
}
return obj;
}
const input = {name: "fff", onlineConsultation: false, image: "", primaryLocation: {locationName: "ggg", street:""}, billingAndInsurance: [], categories: [""], concernsTreated: [""], education: [{nameOfInstitution: "ffff", description: "fff"}], experience: [{from: "", current:""}]}
console .log(removeEmpty(input))
function dropEmptyElements(object) {
switch (typeof object) {
case "object":
const keys = Object.keys(object || {}).filter(key => {
const value = dropEmptyElements(object[key]);
if(value === undefined) {
delete object[key];
}
return value !== undefined;
});
return keys.length === 0 ? undefined : object;
case "string":
return object.length > 0 ? object : undefined;
default:
return object;
}
}
This should do the trick for you ;)

How can I recursively replace the key name in an object?

I am trying to figure out how to use recursion to replace key names of an object with a new key name (and this includes key names inside of nested objects as well). I feel it has something to with the way I'm reassigning to newObj in my first if conditional statement. Any suggestions? Here is my code so far:
// 24. Find all keys in an object (and nested objects) by a provided name and rename
// them to a provided new name while preserving the value stored at that key.
const replaceKeysInObj = (obj, oldKey, newKey, newObj = {}) => {
for(let key in obj){
if (key === oldKey){
newObj[newKey] = obj[key]
}
if (typeof obj[key] === 'object'){
replaceKeysInObj(obj[key], oldKey, newKey);
}
else {
newObj[oldKey] = obj[key]
}
}
return newObj
}
var obj = {'e':{'e':'y'},'l': 'l','y':'e'};
console.log(replaceKeysInObj(obj, 'e', 'new'))
With some modifications to your approach
const replaceKeysInObj = (obj, oldKey, newKey, newObj = {}) => {
if (typeof obj !== "object") return obj;
for (let key in obj) {
newObj[key === oldKey ? newKey : key] = replaceKeysInObj(obj[key], oldKey, newKey);
}
return newObj;
};
const obj = { e: { e: "y" }, l: "l", y: "e" };
console.log(replaceKeysInObj(obj, "e", "new"));
const obj2 = { e: { e: "y" }, l: { e: "y" }, y: "e" };
console.log(replaceKeysInObj(obj2, "e", "new"));
Try this function:
function replaceKeysInObj(obj, oldKey, newKey) {
Object.keys(obj).map((key) => {
if(typeof obj[key] === 'object') {
replaceKeysInObj(obj[key], oldKey, newKey);
}
if(key === oldKey) {
obj[newKey] = obj[oldKey]
delete obj[oldKey];
}
});
return obj;
}
let obj = {'e':{'e':'y'},'l': 'l','y':'e'};
console.log(replaceKeysInObj(obj, 'e', 'new'))
If we think of this a little more generally, we can write a simple recursion. We can write a function to replace all the keys in an arbitrarily-nested object with the result of calling a function on the current keys. We could use this in various ways. If we wanted to convert all keys to upper-case, we would pass it k => k.toUpperCase(). Or for your case, we could write something like k => k == oldKey ? newKey : k. One implementation of this idea could look like this:
const replaceKey = (f) => (o) =>
Array .isArray (o)
? o .map (replaceKey (f))
: Object (o) === o
? Object .fromEntries (Object .entries (o) .map (([k, v]) => [f(k), replaceKey (f) (v)]))
: o
const replaceKeysInObj = (oldKey, newKey) =>
replaceKey (k => k == oldKey ? newKey : k)
const testCase = {foo: 1, bar: {baz: 2, qux: {corge: [{baz: 3}, {baz: 4}]}}}
console.log (
replaceKey (k => k.toUpperCase()) (testCase)
) //~> {FOO: 1, BAR: {BAZ: 2, QUX: {CORGE: [{BAZ: 3}, {BAZ: 4}]}}}
console.log (
replaceKeysInObj ('baz', 'grault') (testCase)
) //~> {foo: 1, bar: {grault: 2, qux: {corge: [{grault: 3}, {grault: 4}]}}}
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that I changed the API of your function a bit. If you wanted the original signature, we could just write
const replaceKeysInObj = (obj, oldKey, newKey) =>
replaceKey (k => k == oldKey ? newKey : k) (obj)
But there is an advantage to how I wrote it, one that I take advantage of often. It lets us partially apply the oldKey and newKey to get a reusable function
const e2new = replaceKeysInObj ('e', 'new')
// later
e2new ({e: {e: 'y'}, l: 'l', y: 'e'}) //=> {new: {new: "y"}, l: "l", y:"e"}
But there's a general point here worth noting: it's often simpler to abstract at least a bit from our current problem, writing a more general solution that is configured with a function or two to fill in the details. Even if we eventually give up the abstraction and inline those configuration functions, it can help us see the problem more clearly.
if you want to change object keys name
i think this is working !
if (oldKey !== newKey) {
Object.defineProperty(obj, newKey, Object.getOwnPropertyDescriptor(obj, oldKey));
delete o[oldKey];
}
If you wish to return a new object with the replaced keys rather than mutating the object, as per the code you have posted, change it to the following:
const replaceKeysInObj = (obj, oldKey, newKey, newObj = {}) => {
for (let key in obj) {
if (key === oldKey) {
newObj[newKey] = obj[key];
} else {
newObj[key] = obj[key];
}
if (typeof obj[key] === 'object') {
newObj[newKey] = replaceKeysInObj(obj[key], oldKey, newKey);
}
}
return newObj;
};
var obj = {
'e': {
'e': 'y',
},
'l': 'l',
'y': 'e',
};
console.log(replaceKeysInObj(obj, 'e', 'new'));
// { new: { new: 'y' }, l: 'l', y: 'e' }
Based on answer of #Scott Sauyet I made one function. You can see example on CodeSandbox. Link for CodeSandbox.
export const replaceKeysInObj = (data, oldKey, newKey) => {
const replaceKey = (f) => (newdata) => {
if (Array.isArray(newdata)) return newdata.map(replaceKey(f));
if (Object(newdata) === newdata) {
return Object.fromEntries(
Object.entries(newdata).map(([key, value]) => [
f(key),
replaceKey(f)(value)
])
);
}
return data;
};
return replaceKey((key) => (key === oldKey ? newKey : key))(data);
};

A better way to trim all elements in an object recursively?

If I have an object like,
const obj = {
field: {
subfield: {
innerObj: { a: ' asdasd asdas . ' },
innerArr: [' s ', ' ssad . '],
innerArrObj: [ { b: ' adsad ' } ],
}
}
}
I've come up with something like this,
const trimFields = (data) =>
_.mapValues(data, (element, k) =>
_.isArray(element)
? element.map((value) =>
_.isObject(value) ? trimFields(value) : trimText(value)
)
: _.isObject(element)
? trimFields(element)
: trimText(element)
);
But I'm wondering if there is a better / more efficient way to do this?
JSFiddle
I'd switch for array / object / other directly in the function, simplifying the recursive call:
const trimFields = (data) =>
_.isArray(data)
? data.map(trimFields)
: _.isObject(data)
? _.mapValues(trimFields)
: trimText(data);
I would write a more general deepMap function and then call it with trimText and your object. It then becomes easily reusable, and it separates out the handling of object navigation from your actual field transformation. It's not hard to write, either with or without lodash. Here's one version:
const deepMap = (fn) => (obj) =>
Array .isArray (obj)
? obj .map (deepMap (fn))
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj) .map (([k, v]) => [k, deepMap (fn) (v)]))
: // else
fn (obj)
const trimText = field => typeof field === 'string' ? field .trim () : field;
const obj = {field: {subfield: {innerObj: { a: ' asdasd asdas . ' }, innerArr: [' s ', ' ssad . '], innerArrObj: [ { b: ' adsad ' } ]}}}
console .log (
deepMap (trimText) (obj)
)
Note that I simplified trimText, since trim is built into String.prototype.
Writing this generic version is pretty much no more difficult than a one-off version, and you can reuse it for other purposes.
deepMap (square) ({a: 1, b: [2, 3, 4], c: [{d: 5}, {d: 6}]})
//=> {a: 1, b: [4, 9, 16], c: [{d: 25}, {d: 36}]}
The lodash's _.transform() function iterates both objects and arrays. You can create a recursive mapValues() function using _.transform(), and then apply a transformer function (_.trim() in this case) to handle all values:
const recursiveMapValues = _.curry((fn, obj) =>
_.transform(obj, (acc, value, key) => {
acc[key] = _.isObject(value) ?
recursiveMapValues(fn, value)
:
fn(value)
}))
const trimFields = recursiveMapValues(v => _.isString(v) ? _.trim(v) : v)
const obj = {"field":{"subfield":{"innerObj":{"a":" asdasd asdas . "},"innerArr":[" s "," ssad . ", 3],"innerArrObj":[{"b":" adsad "}]}}}
const result = trimFields(obj)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

JavaScript: filter() for Objects

ECMAScript 5 has the filter() prototype for Array types, but not Object types, if I understand correctly.
How would I implement a filter() for Objects in JavaScript?
Let's say I have this object:
var foo = {
bar: "Yes"
};
And I want to write a filter() that works on Objects:
Object.prototype.filter = function(predicate) {
var result = {};
for (key in this) {
if (this.hasOwnProperty(key) && !predicate(this[key])) {
result[key] = this[key];
}
}
return result;
};
This works when I use it in the following demo, but when I add it to my site that uses jQuery 1.5 and jQuery UI 1.8.9, I get JavaScript errors in FireBug.
Object.prototype.filter = function(predicate) {
var result = {};
for (key in this) {
if (this.hasOwnProperty(key) && !predicate(this[key])) {
console.log("copying");
result[key] = this[key];
}
}
return result;
};
var foo = {
bar: "Yes",
moo: undefined
};
foo = foo.filter(function(property) {
return typeof property === "undefined";
});
document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, ' ');
console.log(foo);
#disp {
white-space: pre;
font-family: monospace
}
<div id="disp"></div>
First of all, it's considered bad practice to extend Object.prototype. Instead, provide your feature as stand-alone function, or if you really want to extend a global, provide it as utility function on Object, just like there already are Object.keys, Object.assign, Object.is, ...etc.
I provide here several solutions:
Using reduce and Object.keys
As (1), in combination with Object.assign
Using map and spread syntax instead of reduce
Using Object.entries and Object.fromEntries
1. Using reduce and Object.keys
With reduce and Object.keys to implement the desired filter (using ES6 arrow syntax):
Object.filter = (obj, predicate) =>
Object.keys(obj)
.filter( key => predicate(obj[key]) )
.reduce( (res, key) => (res[key] = obj[key], res), {} );
// Example use:
var scores = {
John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1);
console.log(filtered);
Note that in the above code predicate must be an inclusion condition (contrary to the exclusion condition the OP used), so that it is in line with how Array.prototype.filter works.
2. As (1), in combination with Object.assign
In the above solution the comma operator is used in the reduce part to return the mutated res object. This could of course be written as two statements instead of one expression, but the latter is more concise. To do it without the comma operator, you could use Object.assign instead, which does return the mutated object:
Object.filter = (obj, predicate) =>
Object.keys(obj)
.filter( key => predicate(obj[key]) )
.reduce( (res, key) => Object.assign(res, { [key]: obj[key] }), {} );
// Example use:
var scores = {
John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1);
console.log(filtered);
3. Using map and spread syntax instead of reduce
Here we move the Object.assign call out of the loop, so it is only made once, and pass it the individual keys as separate arguments (using the spread syntax):
Object.filter = (obj, predicate) =>
Object.assign(...Object.keys(obj)
.filter( key => predicate(obj[key]) )
.map( key => ({ [key]: obj[key] }) ) );
// Example use:
var scores = {
John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1);
console.log(filtered);
4. Using Object.entries and Object.fromEntries
As the solution translates the object to an intermediate array and then converts that back to a plain object, it would be useful to make use of Object.entries (ES2017) and the opposite (i.e. create an object from an array of key/value pairs) with Object.fromEntries (ES2019).
It leads to this "one-liner" method on Object:
Object.filter = (obj, predicate) =>
Object.fromEntries(Object.entries(obj).filter(predicate));
// Example use:
var scores = {
John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, ([name, score]) => score > 1);
console.log(filtered);
The predicate function gets a key/value pair as argument here, which is a bit different, but allows for more possibilities in the predicate function's logic.
Never ever extend Object.prototype.
Horrible things will happen to your code. Things will break. You're extending all object types, including object literals.
Here's a quick example you can try:
// Extend Object.prototype
Object.prototype.extended = "I'm everywhere!";
// See the result
alert( {}.extended ); // "I'm everywhere!"
alert( [].extended ); // "I'm everywhere!"
alert( new Date().extended ); // "I'm everywhere!"
alert( 3..extended ); // "I'm everywhere!"
alert( true.extended ); // "I'm everywhere!"
alert( "here?".extended ); // "I'm everywhere!"
Instead create a function that you pass the object.
Object.filter = function( obj, predicate) {
let result = {}, key;
for (key in obj) {
if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
result[key] = obj[key];
}
}
return result;
};
Solution in Vanilla JS from year 2020.
let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
You can filter romNumbers object by key:
const filteredByKey = Object.fromEntries(
Object.entries(romNumbers).filter(([key, value]) => key === 'I') )
// filteredByKey = {I: 1}
Or filter romNumbers object by value:
const filteredByValue = Object.fromEntries(
Object.entries(romNumbers).filter(([key, value]) => value === 5) )
// filteredByValue = {V: 5}
If you're willing to use underscore or lodash, you can use pick (or its opposite, omit).
Examples from underscore's docs:
_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}
Or with a callback (for lodash, use pickBy):
_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
return _.isNumber(value);
});
// {age: 50}
ES6 approach...
Imagine you have this object below:
const developers = {
1: {
id: 1,
name: "Brendan",
family: "Eich"
},
2: {
id: 2,
name: "John",
family: "Resig"
},
3: {
id: 3,
name: "Alireza",
family: "Dezfoolian"
}
};
Create a function:
const filterObject = (obj, filter, filterValue) =>
Object.keys(obj).reduce((acc, val) =>
(obj[val][filter] === filterValue ? acc : {
...acc,
[val]: obj[val]
}
), {});
And call it:
filterObject(developers, "name", "Alireza");
and will return:
{
1: {
id: 1,
name: "Brendan",
family: "Eich"
},
2: {
id: 2,
name: "John",
family: "Resig"
}
}
As patrick already stated this is a bad idea, as it will almost certainly break any 3rd party code you could ever wish to use.
All libraries like jquery or prototype will break if you extend Object.prototype, the reason being that lazy iteration over objects (without hasOwnProperty checks) will break since the functions you add will be part of the iteration.
Given
object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};
keys = ['firstname', 'age'];
then :
keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
// {firstname:'abd', age: 16}
// Helper
function filter(object, ...keys) {
return keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
};
//Example
const person = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};
// Expected to pick only firstname and age keys
console.log(
filter(person, 'firstname', 'age')
)
Plain ES6:
var foo = {
bar: "Yes"
};
const res = Object.keys(foo).filter(i => foo[i] === 'Yes')
console.log(res)
// ["bar"]
How about:
function filterObj(keys, obj) {
const newObj = {};
for (let key in obj) {
if (keys.includes(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
Or...
function filterObj(keys, obj) {
const newObj = {};
Object.keys(obj).forEach(key => {
if (keys.includes(key)) {
newObj[key] = obj[key];
}
});
return newObj;
}
I have created an Object.filter() which does not only filter by a function, but also accepts an array of keys to include. The optional third parameter will allow you to invert the filter.
Given:
var foo = {
x: 1,
y: 0,
z: -1,
a: 'Hello',
b: 'World'
}
Array:
Object.filter(foo, ['z', 'a', 'b'], true);
Function:
Object.filter(foo, function (key, value) {
return Ext.isString(value);
});
Code
Disclaimer: I chose to use Ext JS core for brevity. Did not feel it was necessary to write type checkers for object types as it was not part of the question.
// Helper function
function print(obj) {
document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, ' ') + '<br />';
console.log(obj);
}
Object.filter = function (obj, ignore, invert) {
let result = {}; // Returns a filtered copy of the original list
if (ignore === undefined) {
return obj;
}
invert = invert || false;
let not = function(condition, yes) { return yes ? !condition : condition; };
let isArray = Ext.isArray(ignore);
for (var key in obj) {
if (obj.hasOwnProperty(key) &&
!(isArray && not(!Ext.Array.contains(ignore, key), invert)) &&
!(!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) {
result[key] = obj[key];
}
}
return result;
};
let foo = {
x: 1,
y: 0,
z: -1,
a: 'Hello',
b: 'World'
};
print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(foo, (key, value) => Ext.isString(value)));
#disp {
white-space: pre;
font-family: monospace
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script>
<div id="disp"></div>
My opinionated solution:
function objFilter(obj, filter, nonstrict){
r = {}
if (!filter) return {}
if (typeof filter == 'string') return {[filter]: obj[filter]}
for (p in obj) {
if (typeof filter == 'object' && nonstrict && obj[p] == filter[p]) r[p] = obj[p]
else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p]
else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]}
else if (filter.length && filter.includes(p)) r[p] = obj[p]
}
return r
}
Test cases:
obj = {a:1, b:2, c:3}
objFilter(obj, 'a') // returns: {a: 1}
objFilter(obj, ['a','b']) // returns: {a: 1, b: 2}
objFilter(obj, {a:1}) // returns: {a: 1}
objFilter(obj, {'a':'1'}, true) // returns: {a: 1}
objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3}
https://gist.github.com/bernardoadc/872d5a174108823159d845cc5baba337
var foo = {
bar: "Yes",
pipe: "No"
};
const ret = Object.entries(foo).filter(([key, value])=> value === 'Yes');
https://masteringjs.io/tutorials/fundamentals/filter-object
If you have Symbol properties in your object, that should be filtered too, you can not use: Object.keys Object.entries Object.fromEntries, ... because:
Symbol keys are not enumerable !
You could use Reflect.ownKeys and filter keys in reduce
Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});
(Open DevTools for log output - Symbols are not logged on Stackoverflow UI)
const bKey = Symbol('b_k');
const o = {
a: 1,
[bKey]: 'b',
c: [1, 3],
[Symbol.for('d')]: 'd'
};
const allow = ['a', bKey, Symbol.for('d')];
const z1 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && {...a, [k]: o[k]} || a, {});
console.log(z1); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(bKey in z1) // true
console.log(Symbol.for('d') in z1) // true
This is equal to this
const z2 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.assign(a, {[k]: o[k]}) || a, {});
const z3 = Reflect.ownKeys(o).reduce((a, k) => allow.includes(k) && Object.defineProperty(a, k, {value: o[k]}) || a, {});
console.log(z2); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(z3); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
Wrapped in a filter() function, an optional target object could be passed
const filter = (o, allow, t = {}) => Reflect.ownKeys(o).reduce(
(a, k) => allow.includes(k) && {...a, [k]: o[k]} || a,
t
);
console.log(filter(o, allow)); // {a: 1, Symbol(b_k): "b", Symbol(d): "d"}
console.log(filter(o, allow, {e: 'e'})); // {a: 1, e: "e", Symbol(b_k): "b", Symbol(d): "d"}
You could also do something like this where you are filtering on the entries to find the key provided and return the value
let func = function(items){
let val
Object.entries(this.items).map(k => {
if(k[0]===kind){
val = k[1]
}
})
return val
}
If you wish to mutate the same object rather than create a new one.
The following example will delete all 0 or empty values:
const sev = { a: 1, b: 0, c: 3 };
const deleteKeysBy = (obj, predicate) =>
Object.keys(obj)
.forEach( (key) => {
if (predicate(obj[key])) {
delete(obj[key]);
}
});
deleteKeysBy(sev, val => !val);
Like everyone said, do not screw around with prototype. Instead, simply write a function to do so. Here is my version with lodash:
import each from 'lodash/each';
import get from 'lodash/get';
const myFilteredResults = results => {
const filteredResults = [];
each(results, obj => {
// filter by whatever logic you want.
// sample example
const someBoolean = get(obj, 'some_boolean', '');
if (someBoolean) {
filteredResults.push(obj);
}
});
return filteredResults;
};
If you don't need the original object, this is a simple, very boring answer that doesn't waste memory:
const obj = {'a': 'want this', 'b': 'want this too', 'x': 'remove this'}
const keep = new Set(['a', 'b', 'c'])
function filterObject(obj, keep) {
Object.keys(obj).forEach(key => {
if (!keep.has(key)) {
delete obj[key]
}
})
}
If you're only filtering a small number of objects, and your objects don't have many keys, you might not want to bother with constructing a Set, in which case use array.includes instead of set.has.
I just wanted to add the way that I do it because it saves me creating extra functions, I think is cleaner and I didn't see this answer:
let object = {a: 1, b: 2, c: 3};
[object].map(({a,c}) => ({a,c}))[0]; // {a:1, c:2}
The cool thing is that also works on arrays of objects:
let object2 = {a: 4, b: 5, c: 6, d: 7};
[object, object2].map(({a,b,c,d}) => ({a,c})); //[{"a":1,"c":3},{"a":4,"c":6}]
[object, object2].map(({a,d}) => ({a,d})); //[{"a":1,"d":undefined},{"a":4,"d":7}]
In these cases I use the jquery $.map, which can handle objects. As mentioned on other answers, it's not a good practice to change native prototypes (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes)
Below is an example of filtering just by checking some property of your object. It returns the own object if your condition is true or returns undefined if not. The undefined property will make that record disappear from your object list;
$.map(yourObject, (el, index)=>{
return el.yourProperty ? el : undefined;
});

Categories