Saturday, 1 June 2019

Extract - Remove Properties

Recent versions of JavaScript have made it terribly easy to add or overwrite properties of one object with those of another object, by means of Object.prototype.assign (which is also great for shallow cloning). But, what if we want to go in the other sense, create a new object by extracting certain properties from another object? Well, modern JavaScript makes this amazingly concise, let's see:

let source = {a:1, b:2, c:3, d:4};

//picking some values from an object
//notice the extra "()" to prevent it from being considered a block
let picked1 = (({a,b})=>({a, b}))(source);

console.log(picked1);
//{ a: 1, b: 2 }

So, what am I doing there? We declare an arrow function and immediately invoke it. This function uses destructured assignment for its parameters, so it will get the a and b properties of the source object that we are passing over assigned to a and b local variables. Then we put those 2 variables in a new object and return it. Sweet!

There are some additions that we can mix in, like using some default value in case the souce object lacks some of the properties:

//picking and using some default values
source = {a:1};
let picked2 = (({ a="x", c="y" }) => ({ a, c }))();

console.log(picked2);
//{ a: 1, c: 'y' }

And what if the source object is null?

//what if I don't pass anything:
source = null;
try{
 picked = (({ a="x", c="y" }) => ({ a, c }))(source); //it crashes
 console.log(picked);
}
catch(ex){
 console.log("Error");
 //Error: Cannot destructure property `a` of 'undefined' or 'null'.
}

We get an error, as we can not destructure from and undefined/null object. We can control this problem like this:

//in order to control that case where we pass no parameter, we add the extra "= {}" thing
let picked3 = (({ a="x", c="y" } = {}) => ({ a, c }))(); 
console.log(picked3);
//{ a: 'x', c: 'y' }  

We are using an empty object, {}, as parameter to the destructuring operation in case no object is provided (and we use default values for the missing properties). I had already use this technique in this post but at that time I just saw it as magic.

Finally, what if rather than extracting values into a new object we just want to remove them? No magic syntax that I'm aware of, but we can use some rather concise code like this:

function removeProperties(source, propsToRemove){
 return propsToRemove.reduce((res, prop) => {
  delete res[prop];
  return res;
 }, source);
}

let obj = {a:1, b:2, c:3, d:4, e:5, f:6};
let res = removeProperties(obj, ["c", "d"]);
console.log(res);
//{ a: 1, b: 2, e: 5, f: 6 }

No comments:

Post a Comment