Conditionally adding properties to JavaScript object - javascript

Is there a more concise or readable way of doing this?
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
if(foo.a){myobj.a=foo.a;}
if(foo.b){myobj.b=foo.b;}
if(foo.c){myobj.c=foo.c;}
EDIT. Context why I am doing this is below.
var obj={value:this.text,css:{color:'#929292',margin:'1px 0px'}};
if(this.width){obj.css.width=this.width;}
if(this.type){obj.type=this.type;}
if(this.id){obj.id=this.id;}
var input=$('<input/>',obj)

You could use a simple loop-based approach:
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
['a','b','c'].forEach(function(key) {
if(key in foo) {
myobj[key] = foo[key];
}
});
Notice how I used the in keyword. Your current solution will not work if the value of a property is (e.g.) false or 0.
Additionaly, to get better solutions, provide some context: why are you conditionally copying properties? Perhaps you can avoid this to begin with.

With the introduction of the spread operator in ES2018 you can do something like this.
const original = {
first: null,
second: 'truthy'
};
const conditionalObject = {
...( original.first && { first: original.first }),
...( original.second && { second: original.second })
};
In the conditionalObject we first check if the property we want to add from the original is truthy, if it is we spread that property with its value into the new object.
If the property from the original object is falsy, the && short circuits and the property is never spread into the new object.
You can read a more detailed explanation here
The new conditionalObject will look like this
{
second: 'truthy'
}

You can use the ternary operator like this:
myobj.a = foo.a ? foo.a : undefined;
Though its not exactly the same as the if statement you have, because you'll have {a: undefined} instead of {}. The difference would show up if you ever enumerated the keys of your object.
Edit:
#hindmost has a good suggestion in the comments. You could improve it further with underscore.js:
_.extend(myobj, _.pick(foo, ['a', 'b', 'c']));

You could use jQuery's extend, then delete the properties you don't want:
function extendExcept(sourceObject, targetObject, except) {
var target = $.extend(sourceObject, targetObject);
except.forEach(function(key) {
delete target[key];
});
return target;
}
You could call it like:
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
myobj = extendExcept(foo, myobj, ["somePropertyThatShouldntBeAdded"]);
Working Example

var foo = {
a: 111,
c: 333,
somePropertyThatShouldntBeAdded: 'xxx'
};
var myObj = {
x: 1,
y: 2,
z: 3
};
for(var key in foo) {
if(key === 'somePropertyThatShouldntBeAdded') {
continue;
}
myObj[key] = foo[key];
}

Related

How to override object destructuring for ES class just like array destructuring

TL;DR
How do I make {...myCls} return {...myCls.instanceVar}?
Actual Question
I'm trying to implement a custom version of *[Symbol.iterator]() { yield this.internalObj; } such that object-spreads of my class perform an object-spread operation to myClass.instanceVar.
Specifically, I want to make {...(new MyClass('a', 'b'}))} return {...(new MyClass('a', 'b'})).innerVal}. However, it seems we cannot override object-spread logic, we can only override array-spread logic.
For example, this is a simple class to create an Array wrapper
class MyArray {
innerArray = [];
getItem(index) {
return (index < this.innerArray.length) ? this.innerArray[index] : null;
}
setItem(index, val) {
const innerArrayLength = this.innerArray.length;
return (index < innerArrayLength)
?
this.innerArray[index] = val
:
this.innerArray.push(val)
;
}
removeItem(index) {
return this.innerArray.splice(index, 1);
}
clear() {
this.innerArray = [];
}
get length() {
return this.innerArray.length;
}
*[Symbol.iterator]() {
return yield* this.innerArray;
}
}
// Usage:
let myArr = new MyArray() // undefined
myArr.setItem(0, 'a') // 1
myArr.setItem(10, 'b') // 2
console.log([...myArr]) // (2) [ 0 => "a", 1 => "b" ]
However, what I want is a way to do that with object class instance variables instead of array class instance variables.
For example, this is what happens when I try to implement a StorageMock class
class StorageMock {
storage = {};
setItem(key, val) {
this.storage[key] = val;
}
getItem(key) {
return (key in this.storage) ? this.storage[key] : null;
}
removeItem(key) {
delete this.storage[key];
}
clear() {
this.storage = {};
}
get length() {
return Object.keys(this.storage).length;
}
key(index) {
return Object.keys(this.storage)[index] || null;
}
*[Symbol.iterator]() {
return yield* Object.entries(this.storage).map(([ k, v ]) => ({ [k]: v }));
}
}
let myStore = new StorageMock() // undefined
myStore.setItem('a', 'hello'); // undefined
myStore.setItem('b', 'world'); // undefined
console.log({...myStore}); // { storage: { a: "hello", b: "world" } } <== PROBLEM
// Doing the same with localStorage prints out:
// { a: "hello", b: "world" }
// instead of
// { storage: { a: "hello", b: "world" } }
In this case, the Storage API works to spread storage entries when spreading (local|session)Storage, but creating a special StorageMock class does not.
Point being that I can't make {...storageMockInstance} === {...(storageMockInstance.storage)}. So how does one override the object-spreading syntax of an ES class?
References/attempts
I've tried various combinations of Object.create(), Object.definePropert(y|ies)(), variants of the in operator (all of which have relevant access-ability defined here), all depending on the for...in syntax defininition from the generic-spreading-syntax proposal. But all I've found is that only "standard" destructuring can be used according to references 1, 2, and 3.
But there has to be a way to do this via ESNext classes. None of my attempts to accomplish the ability to use actual native class features instead of those available through AMD module syntax. It doesn't seem reasonable that I couldn't override these fields in a way similar to how other languages do so. i.e. If I could only override how the JS for..in loop works in the same way that Python allows overriding it, then I could spread the inner variable through a forIn() method just like toString() or toJSON().
Note
Please do not respond with #babel/polyfill, core-js, or babel-jest for this question. It's not only meant for (local|session)Storage, but also just a question on a high-level problem.
Short answer
You cannot.
Unless you cheat. But might not be worth it.
Actual answer
The term "array destructuring" might be a slightly misleading. It actually always starts by getting the iterator of the object and draws values from there until all bindings are satisfied. In fact, it is not only supposed to be used on arrays.
const obj = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
};
//1. take iterator
//2. take first three values
const [a, b, c] = obj;
//1. take iterator (do not continue the old one)
//2. take the first value
const [x] = obj;
console.log(a, b, c, x); // 1, 2, 3, 1
Object destructuring, however, does not have a similar mechanism. When using {...x} the abstract operation CopyDataProperties is performed. As the name suggests, it will copy properties, rather than invoke some mechanism to get the data to copy.
The properties that will be copied would be
Own - not coming from the prototype.
A data properties as opposed to an accessor properties (a property defined by a getter and/or setter).
Enumerable.
Not part of the excludedNames parameter to the abstract operation. Note: this is only relevant when using spread with a rest target, like const {foo, bar, ...rest} = obj;
What could be done is to lie to the runtime about each of these things. This can be done using a Proxy and you need to change the following things:
ownKeys trap to switch what keys would be used for the destructuring.
getOwnPropertyDescriptor trap to make sure the properties are enumerable.
get trap to give the value for the property.
This can be done as a function like this, for example and will make an object behave as if you are using one of its property values:
const obj = {
a: 1,
b: 2,
c: 3,
myProp: {
d: 4,
e: 5,
f: 6
}
};
const x = { ...lieAboutSpread("myProp", obj) };
console.log(x);
function lieAboutSpread(prop, obj) {
//this will be the false target for spreading
const newTarget = obj[prop];
const handler = {
// return the false target's keys
ownKeys() {
return Reflect.ownKeys(newTarget);
},
// return the false target's property descriptors
getOwnPropertyDescriptor(target, property) {
return Reflect.getOwnPropertyDescriptor(newTarget, property);
},
// return the false target's values
get(target, property, receiver) {
return Reflect.get(newTarget, property, receiver);
}
}
return new Proxy(obj, handler);
}
So, this is possible. I am however, not sure it is of that much benefit to simply doing { ...obj.myProp }. Moreover, the above function could be re-written in a way that does not "lie" at all. But it becomes extremely boring:
const obj = {
a: 1,
b: 2,
c: 3,
myProp: {
d: 4,
e: 5,
f: 6
}
};
const x = { ...lieAboutSpread("myProp", obj) };
console.log(x);
function lieAboutSpread(prop, obj) {
//this will be the target for spreading
return obj[prop];
}
In my opinion, this highlights why the artificial way of masking the object is an overkill.

ES6 syntax reference: use spread and boolean short circuiting to conditionally add fields to an object during declaration

I want to construct an object like this:
const obj = {
a: 'a', // only add this if "someCondition" is true
b: 'b', // only add this if "someCondition" is false
always: 'present', // add this in any case
}
This works:
const obj = { always: 'present' }
if (someCondition) { obj.a = 'a' }
if (!someCondition) { obj.b = 'b' }
However, I'm looking for a more concise way using ES6 syntax.
It is possible using ES6 syntax to conditionally add fields during declaration of an object.
This is useful if the consumer of the object will not tolerate fields with null / undefined / whatever values, and you do not want to have to write multiple statements to correctly declare the object:
const obj = {
...(someCondition && {a: 'a'}),
...(!someCondition && {b: 'b'}),
always: 'present'
}
So how does that work ? Lets look at ...(true && {a: 'a'}). The ES6 spread operator "..." will iterate each of the field->value pairs in { "a": "a" } applying them to x.
The true && x expression will return x, whereas false && x will return false. This is known as short circuit evaluation
So if the logical expression is true then the spread operator will add the fields, and if it is not true it will not add anything.

Javascript creating a normal variable fallback if object property does not exist

So now I nailed down the basics of javascript and I'm ready to get into the more intermediate arts of coding with "style". I'm trying to write easy maintainable code. The idea is to make a function work even if one of the object properties in use is not available by creating fallbacks. Problem is if I access the properties often then I would have to create ternary conditionals that create a simple for each accessed property. Right now you can see I'm only accessing object.a. I could of course store all the accessing of properties:
Idea 1
var a = (object.hasOwnProperty(a) ? object.a : a)
var b ...
var c ...
idea 2:
var a = (object['a'] ? object.a : a)
idea 3:
var a = object['a'] ? object.a : a
Idea 3:
(object.hasOwnProperty(a) ? var a = object.a : var a = 1);
Idea 4:
Switch statements?
At last:
object = {
// a: 1,
b: 2,
c: 3,
}
// normal vars in case one of the properties do not exist
var a = 1,
b = 2,
c = 3;
function x(){
var a = 1;
object.a * 10
if (object.a == not exist || (object.b == not exist|| (object.c == not exist)
then treat all non existing object properties accessed to as normal variables by doing:
convert object.a --> a
{
The ternary operator is a great way to do this. It allows you the ability to evaluate any expression you like to determine whether your candidate value is appropriate or whether you should use a fallback.
Examples...
// Use fallback if candidate is not "truthy"
var result = obj.a ? obj.a : "fallback";
// Use fallback if candidate is undefined
var result = obj.a !== undefined ? obj.a : "fallback";
// Use fallback if candidate is not defined on the object (whether or not it exists in the prototype chain)
var result = obj.hasOwnProperty(a) ? obj.a : "fallback";
You need to decide what condition you'd like to use a fallback value. Once you decide, wrap it in a function. Or make several similar functions which use different conditions.
Here's a function which checks to see if the candidate value is undefined and returns a fallback value.
function getOrDefault(candidate, fallback) {
if (typeof candidate === "undefined") {
return fallback;
}
return candidate;
}
// Example 1
var b = "alternate value";
var obj = { foo: "value" };
var result = getOrDefault(obj.a, b);
// result -> "alternate value";
// Example 2
var b = "alternate value";
var obj = { a: false };
var result = getOrDefault(obj.a, b);
// result -> false;
Also worth looking into is lodash's get function. Allows you to check for the existence of a property (even deeply nested properties) and allows you to specify a fallback.
ES6 provides nice facilities for doing what you want. The simplest example is
var { a = 1, b = 2 } = obj;
This uses destructuring assignment with defaults. The property a on obj is retrieved and assigned to variable a, but it if doesn't exist, a takes on the value 1.
Expanding on my comments about an "extend" or "merge" function, let's take a look at the following (from here):
var extend = function ( defaults, options ) {
var extended = {};
var prop;
for (prop in defaults) {
if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
extended[prop] = defaults[prop];
}
}
for (prop in options) {
if (Object.prototype.hasOwnProperty.call(options, prop)) {
extended[prop] = options[prop];
}
}
return extended;
};
// Your "fallback" object values
var defaults = {
a: 1,
b: 2
};
// Your target object
var myObject = {
b: 4,
c: 8
};
// one line to override a set of defaults, producing a final object
console.log(extend(defaults, myObject)); // Object { a: 1, b: 4, c: 8 }

Remove empty properties / falsy values from Object with Underscore.js

I have an object with several properties. I would like to remove any properties that have falsy values.
This can be achieved with compact on arrays, but what about objects?
Since Underscore version 1.7.0, you can use _.pick:
_.pick(sourceObj, _.identity)
Explanation
The second parameter to _.pick can be a predicate function for selecting values. Values for which the predicate returns truthy are picked, and values for which the predicate returns falsy are ignored.
pick _.pick(object, *keys)
Return a copy of the object, filtered to only have values for the whitelisted keys (or array of valid keys). Alternatively accepts a predicate indicating which keys to pick.
_.identity is a helper function that returns its first argument, which means it also works as a predicate function that selects truthy values and rejects falsy ones. The Underscore library also comes with a bunch of other predicates, for instance _.pick(sourceObj, _.isBoolean) would retain only boolean properties.
If you use this technique a lot, you might want to make it a bit more expressive:
var pickNonfalsy = _.partial(_.pick, _, _.identity); // Place this in a library module or something
pickNonfalsy(sourceObj);
Underscore version 1.6.0 provided _.pick as well, but it didn't accept a predicate function instead of a whitelist.
You could make your own underscore plugin (mixin) :
_.mixin({
compactObject: function(o) {
_.each(o, function(v, k) {
if(!v) {
delete o[k];
}
});
return o;
}
});
And then use it as a native underscore method :
var o = _.compactObject({
foo: 'bar',
a: 0,
b: false,
c: '',
d: null,
e: undefined
});
Update
As #AndreiNeculau pointed out, this mixin affects the original object, while the original compact underscore method returns a copy of the array.
To solve this issue and make our compactObject behave more like it's cousin, here's a minor update:
_.mixin({
compactObject : function(o) {
var clone = _.clone(o);
_.each(clone, function(v, k) {
if(!v) {
delete clone[k];
}
});
return clone;
}
});
Quick 'n Clear: _.omitBy( source, i => !i );
This is stated in an inverse fashion to Emil's answer. This way imho reads clearer; it's more self explanatory.
Slightly less clean if you don't have the luxury of ES6: _.omitBy( source, function(i){return !i;});
Alternate: _.omitBy( source, _.isEmpty)
Using _.isEmpty, instead of _.identity for truthiness, will also conveniently remove empty arrays and objects from the collection and perhaps inconveniently remove numbers and dates. Thus the outcome is NOT an exact answer to the OP's question, however it could be useful when looking to remove empty collections.
With lodash's transform,
_.transform(obj, function(res, v, k) {
if (v) res[k] = v;
});
Object.keys(o).forEach(function(k) {
if (!o[k]) {
delete o[k];
}
});
You can create a shallow clone:
_(obj).reduce(function(a,v,k){
if(v){ a[k]=v; }
return a;
},{});
for object use delete.
for(var k in obj){
if(obj.hasOwnProperty(k) && !obj[k]){
delete obj[k];
}
}
Suddenly I needed create a function to remove recursively falsies. I hope this helps. I'm using Lodash.
var removeFalsies = function (obj) {
return _.transform(obj, function (o, v, k) {
if (v && typeof v === 'object') {
o[k] = _.removeFalsies(v);
} else if (v) {
o[k] = v;
}
});
};
_.mixin({ 'removeFalsies': removeFalsies });
Then you can use it:
var o = _.removeFalsies({
foo: 'bar',
a: 0,
b: false,
c: '',
d: null,
e: undefined,
obj: {
foo: 'bar',
a: 0,
b: false,
c: '',
d: null,
e: undefined
}
});
// {
// foo: 'bar',
// obj: {
// foo: 'bar'
// }
// }
To add to gion_13's answer:
_.mixin({
compactObject : function(o) {
var newObject = {};
_.each(o, function(v, k) {
if(v !== null && v !== undefined) {
newObject[k] = v
}
});
return newObject;
}
});
This one creates a new object and adds keys and values instead of cloning everything and deleting key-value pairs. Minor difference.
But more importantly, checks explicitly for null and undefined instead of falsey, which will delete key-value pairs that have false as a value.
You should use pickBy from lodash.
The default signature of pickBy is _.(sourceObj, [_.identity]) so it will remove empty properties
_.pickBy(sourceObj);
in the lodash you do like this:
_.pickBy(object, _.identity);
I do understand the question is asking specifically how to do this with underscore, but since I came here from google, I wanted to add how to do this using ES6, plus, I really like this method
Object.fromEntries(Object.entries(obj).filter(([k,v],i)=>v))
The reason is because it creates a new Object (I want it to create a new object without modifying the previous one), uses no libraries, and defines no variables, it doesn't pollute the current scope.
Although _.compact is documented for use in arrays. It seems to work for objects too. I just ran the following in chrome, opera and firefox consoles:
var obj = {first: 1, second: null, third: 3, fourth: function(){return 5}}
undefined
_.compact(obj)
[1, 3, function()]
UPDATE: As the sample indicates calling _.compact on an object will drop the keys and return a compacted array.

Merge two object literals in javascript

I have two object literals:
var animal = {
eat: function() {
console.log("eating...");
}
}
var dog = {
eat: "this has to be replaced when merged",
nrOfLegs: 4
}
Need a merging function like this:
dog = someMergingFunction(animal, dog);
That produces:
{
eat: function() {
console.log("eating...");
},
nrOfLegs: 4
}
One of the object literals has to replace identical properties.
How do I do this in Javascript?
The following should work:
function merge(obj1, obj2) {
var obj = {};
for (var x in obj1)
if (obj1.hasOwnProperty(x))
obj[x] = obj1[x];
for (var x in obj2)
if (obj2.hasOwnProperty(x))
obj[x] = obj2[x];
return obj;
}
If both objects have the same property, the value in obj2 takes precedence.
I highly recommend jQuery's extend method, as it will provide a full browser support.
var object = $.extend({}, object1, object2, ..., objectN);
Remember that the first argument is the target. The good point about usage of extend is that by following code, you can make it extend recursively:
var object = $.extend(object, object1, object2, ..., objectN);
See the jQuery's documentation for more info: jQuery Docs for Extend method
// usage merged = someMergingFunction(a, b, c, d, ...)
// keys in earlier args override keys in later args.
// someMergingFunction({foo:"bar"}, {foo:"baz"})
// ==> {foo:"bar"}
function someMergingFunction () {
var o = {}
for (var i = arguments.length - 1; i >= 0; i --) {
var s = arguments[i]
for (var k in s) o[k] = s[k]
}
return o
}
Assume properties of the first parameter will override properties of the 2nd parameter (as your example), this will do:
function merge(obj1, obj2) {
for(attr in obj1)
obj2[attr]=obj1[attr];
return obj2;
}
I recommend using underscore.js as it contains functionality for this and a whole load of related things:
_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
http://underscorejs.org/#extend
As of 2017, I would use Object.assign(foo, bar)
What about spread syntax ?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
This might be swatting a fly with a buick, but you might be interested to see how Dojo does basically the same thing in dojo.mixin (at least if I understood the question correctly).
https://github.com/dojo/dojo/blob/0dddc5a0bfe3708e4ba829434602da51cbb041b7/_base/_loader/bootstrap.js#L277-366
The basic functionality is in dojo._mixin, while dojo.mixin makes it work iteratively for multiple objects progressively in one shot.
Note that dojo.mixin operates in the opposite direction to what you hinted at in your example.
There are some good suggestions here.
I know this is a really old question, but for future visitors looking for a slightly more flexible solution, I have a similar function that I wrote that accepts any number of objects in an array and merges them all together and returns a single object with the properties of all of the object literals in the array.
Note: the order of precedence is determined by the array. Each subsequent object will overwrite identical properties if they exist in previous objects. Otherwise, new properties are simply added to the single object that is returned.
I hope this will help future visitors to this question. Here's the function, very short and sweet:
var mergeObjects = function (objectsArray) {
var result = {};
for (var i = 0; i < objectsArray.length; i++) {
for (var obj in objectsArray[i]) {
if (objectsArray[i].hasOwnProperty(obj)) {
result[obj] = objectsArray[i][obj];
};
};
};
return result;
};
You can use it like this:
// Define the mergeObjects function
var mergeObjects = function (objectsArray) {
var result = {};
for (var i = 0; i < objectsArray.length; i++) {
for (var obj in objectsArray[i]) {
if (objectsArray[i].hasOwnProperty(obj)) {
result[obj] = objectsArray[i][obj];
};
};
};
return result;
};
// Define some objects to merge, keeping one property consistent so you can
// see it overwrite the old ones
var obj1 = { test1: "test", overwrite: "overwrite1" };
var obj2 = { test2: "test2", overwrite: "overwrite2" };
var obj3 = { test3: "test3", overwrite: "overwrite3" };
// Merge the objects
var newObject = mergeObjects([obj1, obj2, obj3]);
// Test the output
for (var obj in newObject){
if (newObject.hasOwnProperty(obj)){
document.body.innerHTML += newObject[obj] + "<br />";
}
}

Categories