indexOf but for objects? [duplicate] - javascript

This question already has answers here:
What's the correct way to test for existence of a property on a JavaScript Object?
(3 answers)
Closed 6 years ago.
I want to look through an object and assign each of it's existent properties to a variable.
There are 4 possible properties. Some of the objects have all 4. Some might only have two.
How can I check if a particular property exists? Is there an equivalent of indexOf() for arrays but for objects instead?

Use the in keyword:
"key" in object
which returns true or false, depending if the object, or anything in its prototype chain, has that property.
You can also use object.hasOwnProperty("key"), which will only be true if the object has key as a property of itself, not its prototype. Example:
var object = {};
"toString" in object; // true
object.hasOwnProperty("toString"); // false
Note (as per #dandavis's comment) that if object has a custom property called hasOwnProperty, this gets thwarted; to work around this, use hasOwnProperty.call(object, "key"). Example:
var a = {hasOwnProperty: Boolean};
a.hasOwnProperty('name'); // true
hasOwnProperty.call(a, 'name'); // false

If you are only interested in properties set directly on the object (not accessible via the prototype chain) then hasOwnProperty will provide a boolean value, true, if an object has the specified property.
For example: testObject.hasOwnProperty('propertyToCheckFor') would return true if testObject.propertyToCheckFor exists, otherwise it would be false.
See the following code for a more expanded example:
var obj1 = {
a: 1
};
var obj2 = {
a: 1,
b: 2
};
var obj3 = {
b: 2,
c: 3
};
var obj4 = {
a: 1,
b: 2,
c: 3
};
// For dispaly purposes
document.write('<pre>' + JSON.stringify({
obj1: {
hasA: obj1.hasOwnProperty('a'),
hasB: obj1.hasOwnProperty('b'),
hasC: obj1.hasOwnProperty('c')
},
obj2: {
hasA: obj2.hasOwnProperty('a'),
hasB: obj2.hasOwnProperty('b'),
hasC: obj2.hasOwnProperty('c')
},
obj3: {
hasA: obj3.hasOwnProperty('a'),
hasB: obj3.hasOwnProperty('b'),
hasC: obj3.hasOwnProperty('c')
},
obj4: {
hasA: obj4.hasOwnProperty('a'),
hasB: obj4.hasOwnProperty('b'),
hasC: obj4.hasOwnProperty('c')
}
}, null, 2) + '</pre>');

var obj = {
foo: 1,
bar: 2,
baz: 3
}
Object.keys(obj).forEach(function(key) {
window[key] = obj[key]
})
console.log(foo, bar, baz)
Or in ES2015
const obj = {
foo: 1,
bar: 2,
baz: 3
}
function assignPrivateVars() {
let {foo, bar, baz} = obj;
console.log(foo, bar, baz);
}
assignPrivateVars();

You can use destructuring assignment. If value is not defined, variable will be set to undefined. You can also check if variable is defined after destructuring then delete variable by reference.
var data = {a:1, b:2, c:3};
var {a, b, c, d} = data; // `d`: `undefined`

Related

Destructure and retrieve the fully structured variable in one expression [duplicate]

This question already has answers here:
Destructure object parameter, but also have reference to the parameter as an object? [duplicate]
(2 answers)
Closed 3 years ago.
Is it possible to both destructure a variable and have access to the structured variable in the same call?
For example, what could I replace ??? below to get the desired output (and how else might I have to edit my code)
const foo = ({ a, b }) => {
console.log(a) // 1
console.log(b) // 2
console.log(???) // { a: 1, b: 2 }
}
const x = { a: 1, b: 2 }
foo(x)
My goal is knowledge and succinct code - I want to avoid const { a, b } = params as the first line of foo() in the case where I might need to pass the entire params object on.
If the first argument is an object whose reference you want, it's not possible - once you destructure an argument, there's no longer any way to reference to the full argument, only some of its properties.
It would be possible if the object and parts you wanted was part of a larger object, though, because then you can reference the property name alone (to get the object into a variable), and then reference it again to destructure, eg:
const foo = ({ obj: { a, b }, obj }) => {
console.log(a) // 1
console.log(b) // 2
console.log(obj) // { a: 1, b: 2 }
}
const obj = { a: 1, b: 2 }
foo({ obj })
Your original code could work to an extent if the object had a property that referenced itself, but that's pretty weird:
const foo = ({ a, b, obj }) => {
console.log(a) // 1
console.log(b) // 2
console.log(obj) // { a: 1, b: 2 }
}
const x = { a: 1, b: 2 }
x.obj = x;
foo(x)

Enum using empty objects

We can create "Enums" in Javascript as follows:
var MyEnum = {
A: 0,
B: 1,
}
Can I use empty objects instead of numbers as follows?
var MyEnum = {
A: {},
B: {},
}
What's the difference and which should be used? There isn't any specific use case.
I changed my answer after you edited because just noticed what you are trying.
Yes you can use objects as enum value without any problem. When you define {} in an object, it creates and empty object with unique reference.
{} != {} won't be equal because two different objects with same doesn't mean they are same object. Two red balls you have, these balls are the same ball? No.
But instance.type == MyEnum.ObjectEnum1 will always be true.
Because both instance and the MyEnum object shares the same reference to the object.
var MyEnum = {
A: 1,
B: 2,
C: 3,
ObjectEnum1: {},
ObjectEnum2: {}
}
var obj1 = {
type: MyEnum.B
}
var obj2 = {
type: MyEnum.C
}
var obj3 = {
type: MyEnum.ObjectEnum1
}
console.log(obj1.type == MyEnum.B); //Should be true
console.log(obj2.type == MyEnum.A); //Should be false
console.log(obj2.type == MyEnum.C); //Should be true
console.log(obj3.type == MyEnum.ObjectEnum1); //Should be true
console.log(obj3.type == MyEnum.ObjectEnum2); //Should be false

Extending object's properties without overwriting them

I'm trying to extend the keys/values in target object (with the keys/values) from source object, but without overwriting existing keys/values in the target object. Meaning:
var obj1 = {
a: 1,
b: 2
};
var obj2 = {
b: 4,
c: 3
};
extend(obj1, obj2);
console.log(obj1); // --> {a: 1, b: 2, c: 3}
Interestingly, I found Object.assign(obj1,obj2);, but it overwrites the keys/values.
I need to not overwrite them, if existent and add them if nonexistent.
Please help in plain JavaScript.
Just a simple loop. If you only want enumerable own properties, then:
Object.keys(obj2).forEach(function(key) {
if (!(key in obj1)) {
obj1[key] = obj2[key];
}
});
If you want all enumerable properties:
var key;
for (key in obj2) {
if (!(key in obj1)) {
obj1[key] = obj2[key];
}
}
The key (no pun) bit there is the in operator, which tells you whether an object has a property (of its own, or via inheritance).
There's also Object.prototype.hasOwnProperty which would tell you only if the object has its own (not inherited) property with a given name:
Object.keys(obj2).forEach(function(key) {
if (!obj1.hasOwnProperty(key)) {
obj1[key] = obj2[key];
}
});
More:
in operator
hasOwnProperty
There is no such built in functions available in JS to do that for you.
You have to write your own logic to do that,
var x = {a:10};
var y = {a:5, b: 20};
merge(x,y);
function merge(objSrc, objTarget){
return Object.keys(objTarget).reduce(function(src, prop){
if(!src.hasOwnProperty(prop)) src[prop] = objTarget[prop];
return src;
}, objSrc);
}
console.log(x); {a:10, b:20}
P.S The above code would do a merge over enumerable own properties since Object.keys() would return the same.

Shorthand for Object.create() with multiple properties

If I want to create an object in JavaScript that has a prototype link to another object, but has several of it's own properties how can I do this?
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create( object1 );
object2.c = 3;
object2.d = 4;
console.log( object2 ); // my new object with object1 as it's prototype link
My challenge here is that I have to set object2's properties one at a time.
My other option is:
var object1 = {
a: 1,
b: 2
};
var object2 = {
c: 3,
d: 4
};
Object.setPrototypeOf( object2, object1 );
console.log( object2 );
My challenge above is that the performance is supposed to be terrible. Namely, setPrototypeOf is slow. https://jsperf.com/object-create-vs-object-setprototypeof
And then of course, there's the "shorthand" where you provide, writeable, enumerable and all that to Object.create(), but that's not really shorthand.
Any ideas?
You can combine Object.create with Object.assign for this:
var object2 = Object.assign(Object.create(object1), {
c: 3,
d: 4
});
As an alternative to Object.assign, remember Object.create accepts a second argument with the property descriptors you want to add to the object:
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create(object1, {
c: {value: 3, enumerable: true},
d: {value: 4, enumerable: true}
});
console.log( object2 ); // my new object with object1 as it's prototype link
Note the default is non-configurable, non-writable and non-enumerable.
If that's a problem, ES2017 introduces Object.getOwnPropertyDescriptors.
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create(object1, Object.getOwnPropertyDescriptors({
c: 3,
d: 4
}));
console.log( object2 ); // my new object with object1 as it's prototype link
A more elegant way of doing this would be by using spread syntax.
const obj1 = { a: 1, b: 2 }
const obj2 = { ...obj1, c: 3, d: 4 }
console.table(obj1)
console.table(obj2)
You can even add more properties to the same object in a similar way.
let obj = { a: 1, b: 2 }
obj = { ...obj, c: 3, d: 4 }
console.table(obj)
This also works for arrays.
let arr = [1, 2]
arr = [...arr, 3] // Equivalent to Array.push()
arr = [0, ...arr] // Equivalent to Array.unshift()
arr = [-1, ...arr, 4] // Equivalent to simultaneous Array.unshift() and Array.push()
console.table(arr)
Normally, when we talk about setting and swapping prototypes, we are talking about constructor functions that are instantiated into objects and not object literals themselves.
You can certainly, just manually switch the prototype yourself in this case (which is the basis for prototypical inheritance) and will cause you to inherit the right properties, but you also now have to deal with constructor issues when instances of your derived object get made.
But, this technique is fast as it only requires a new instance to be made and that reference is then set in the prototype property.
function object1(){
this.a = 1;
this.b = 2;
console.log("object1 has been invoked");
};
function object2(){
console.log("object2 has been invoked");
this.c = 3;
this.d = 4;
};
// It's very important that the prototype be set to a NEW instance
// of the super-object so that you don't wind up sharing a prototype
// with other unintended objects.
object2.prototype = new object1();
// object2.prototype.constructor was the function object2
// But now that object2 has had its prototype swapped out
// to object1, when new instances of object2 are made, the
// constructor for object1 will execute. To fix this, we
// just need to reset the constructor property of the new
// prototype that we just set. That's another reason we created
// a new instance of object1, so we could modify the constructor
// of that instance without screwing up other instances of object1
// that may be in use. object2 will use object1 as
// it's prototype, but that particular instance of object1
// will have object2 set as the constructor to use when instances
// are needed.
object2.prototype.constructor = object2;
console.log( new object2() );

Reset object to empty object keeping references intact in non-linear time

Let's say I have obj1 with only enumerable properties:
var obj1 = { a: 1, b: 2, c: 3 };
I then create obj2 which holds a reference to obj1:
var obj2 = obj1;
obj1 === obj2; // true
At some point, I want to reset obj1 to an empty object:
for (key in obj1) {
delete obj1[key];
}
obj1 === obj2; // true
But I'd like to avoid having to iterater over all the properties:
obj1 = {};
obj1 === obj2; // false (this obviously doesn't work)
Is there another solution?
If you have flexibility on the data model, then store the actual properties in a child object:
var obj1 = {theData: { a: 1, b: 2, c: 3} };
then you can reset the properties with obj1.theData = {}.
Of course, that implies that access to any property will incur an additional "hop", so depending on how often you access the data (read or write it) vs. reset the object, you might be better off keeping the delete loop.
Other than that, I don't believe you can reset an object like you can an Array (via a.length=0).

Categories

Resources