Setting property value from other property value inside object - javascript

Is there a way to set the value of a property within an object relative to another property within the same object?
For example:
var myObj = {
prop1 = 10,
prop2 = prop1 + 5
};
alert(myObj.prop2); //Alerts 15
Another example would be for when I create objects within an object aswell
var mainObj = {
obj1: {x: 100, y:100},
obj2: {x: obj1+10, y: obj1+10}
};
alert(mainObj.obj2.x); //Alerts 110
I want certain properties to stay relative to other properties in the same object, so that when I change one property, it will affect the other property.

You can declare your properties as functions
var obj = {
prop1:1,
prop2:function(){
return this.prop1 + 1;
}
}

If you don't care about backwards compatibility with browsers that only support ECMAScript 3, you can use accessors (setters and getters) featured in ECMAScript 5:
var myObj = Object.create(null, {
prop1: {
writable: true,
configurable: true,
value: 10
}, prop2: {
get: function() {
return this.prop1 + 5;
}
}
});
myObj.prop1 // 10
myObj.prop2 // 15
myObj.prop1 = 20 // 20
myObj.prop2 // 25
myObj.prop2 = 6 // 6
myObj.prop2 // 25
See also the Object.defineProperty() method, which allows you to add accessors to already existing objects.

Related

Indexing a JSON by index instead of key? [duplicate]

If I have an array like this:
var arr = ['one','two','three'];
I can access different parts by doing this:
console.log(arr[1]);
How can I access object properties by their order rather than by key?
Example:
var obj = {
'something' : 'awesome',
'evenmore' : 'crazy'
},
jbo = {
'evenmore' : 'crazy',
'something' : 'awesome'
};
How would I get the first property for each object–"something" from obj and "evenmore" from jbo–without explicitly using the property name?
Now, a few of you seem to think I'm after something like:
console.log(obj['something']);
This is not the case, I'm specifically looking to target the index, just like the first example - if it's possible.
"I'm specifically looking to target the index, just like the first example - if it's possible."
No, it isn't possible.
The closest you can get is to get an Array of the object's keys, and use that:
var keys = Object.keys( obj );
...but there's no guarantee that the keys will be returned in the order you defined. So it could end up looking like:
keys[ 0 ]; // 'evenmore'
keys[ 1 ]; // 'something'
The only way I can think of doing this is by creating a method that gives you the property using Object.keys();.
var obj = {
dog: "woof",
cat: "meow",
key: function(n) {
return this[Object.keys(this)[n]];
}
};
obj.key(1); // "meow"
Demo: http://jsfiddle.net/UmkVn/
It would be possible to extend this to all objects using Object.prototype; but that isn't usually recommended.
Instead, use a function helper:
var object = {
key: function(n) {
return this[ Object.keys(this)[n] ];
}
};
function key(obj, idx) {
return object.key.call(obj, idx);
}
key({ a: 6 }, 0); // 6
You can use the Object.values() method if you dont want to use the Object.keys().
As opposed to the Object.keys() method that returns an array of a given object's own enumerable properties, so for instance:
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.keys(object1));
Would print out the following array:
[ 'a', 'b', 'c' ]
The Object.values() method returns an array of a given object's own enumerable property values.
So if you have the same object but use values instead,
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.values(object1));
You would get the following array:
[ 'somestring', 42, false ]
So if you wanted to access the object1.b, but using an index instead you could use:
Object.values(object1)[1] === 42
You can read more about this method here.
var obj = {
'key1':'value',
'2':'value',
'key 1':'value'
}
console.log(obj.key1)
console.log(obj['key1'])
console.log(obj['2'])
console.log(obj['key 1'])
// will not work
console.log(obj.2)
Edit:
"I'm specifically looking to target the index, just like the first example - if it's possible."
Actually the 'index' is the key. If you want to store the position of a key you need to create a custom object to handle this.
Yes, it is possible. We can define getters for each index, and return the property value, in the constructor method of the class. See this code.
class className {
constructor() {
this.name = "Bikram";
this.age = 8;
this.x = 89;
this.y = true;
//Use a for loop and define the getters (with the object property's index as its "name") for each property using Object.defineProperty()
for (let i = 0; i < Object.keys(this).length; i++) {
Object.defineProperty(this, i, {
get: function() {
return Object.values(this)[i]}
});
}
}
}
var b = new className();
console.log(b[0]); // same as b.name ("Bikram")
console.log(b[1]); // = b.age (8)
console.log(b[2]); // = b.x (89)
console.log(b[3]); // = b.y (true)
Edit: If you want to change the properties by their indices, which, of course, you do. Then, just define a corresponding setter for each property in the Object.defineProperty() method. It will look like:
// Insert this in place of the old one
Object.defineProperty(this, i, {
get: function() {
return Object.values(this)[i];
},
set: function(newValue) {
this[Object.keys(this)[i]] = newValue;
}
})
console.log(b[0]); // "Bikram"
b[0] = "Bikram Kumar";
console.log(b[0]); // "Bikram Kumar"
And now you have an "array-like-object" whose properties can be accessed or modified either by property key or its index :D
A side note: Notice that Object.keys() and Object.values() only return the enumerable properties. If you just declare a property and not assign it to any value, the Object.[key/value]s() methods will leave that in the returned array, because by default they are not enumerable. This might become confusing for the indices so defined (except the case the undeclared property is the last one).
To get around this, there is a simple way, if you want some property to have a index, but don't wanna assign it now. Just set it to undefined, and it will now be enumerable, and the indices won't be affected.
by jquery you can do this:
var arr = $.map(obj,function(value, key) {
return value;
});
alert(obj[0]);
Get the array of keys, reverse it, then run your loop
var keys = Object.keys( obj ).reverse();
for(var i = 0; i < keys.length; i++){
var key = keys[i];
var value = obj[key];
//do stuff backwards
}
you can create an array that filled with your object fields and use an index on the array and access object properties via that
propertiesName:['pr1','pr2','pr3']
this.myObject[this.propertiesName[0]]
I went ahead and made a function for you:
Object.prototype.getValueByIndex = function (index) {
/*
Object.getOwnPropertyNames() takes in a parameter of the object,
and returns an array of all the properties.
In this case it would return: ["something","evenmore"].
So, this[Object.getOwnPropertyNames(this)[index]]; is really just the same thing as:
this[propertyName]
*/
return this[Object.getOwnPropertyNames(this)[index]];
};
let obj = {
'something' : 'awesome',
'evenmore' : 'crazy'
};
console.log(obj.getValueByIndex(0)); // Expected output: "awesome"
Sure it is possible, but it is not as immediate as accessing to an array by its indexes, but still possible and even relatively simple actually: in fact you don't have to struggle too much. This code sample will show how:
var obj = {
'alfa' : 'value of obj the key alfa',
'beta' : 'value of obj the key beta',
'gamma' : 'value of obj the key gamma'
};
var jbo = {
'alfa' : 'value of jbo the key alfa',
'beta' : 'value of jbo the key beta',
'gamma' : 'value of jbo the key gamma'
};
alert ( obj[Object.keys(obj)[1]] );
alert ( jbo[Object.keys(jbo)[1]] );
/* you can even put it into a for loop as follows */
for (i=0;i<3;i++)
{
document.writeln ( "<br>This could be even a piece of HTML: " + obj[Object.keys(obj)[i]] );
document.writeln ( "<br>This could be even a piece of HTML: " + jbo[Object.keys(jbo)[i]] );
}
Explication:
As you know the Object.keys() statement returns an array of all enumerable properties (which means all keys) of the object you type into its round parenthesis.
So the only thing you need is to indicate the index after that array, which will returns the key literal found at that index.
The key itself is "digested" as usual by the object which returns the value at that key.
If you are not sure Object.keys() is going to return you the keys in the right order, you can try this logic instead
var keys = []
var obj = {
'key1' : 'value1',
'key2' : 'value2',
'key3' : 'value3',
}
for (var key in obj){
keys.push(key)
}
console.log(obj[keys[1]])
console.log(obj[keys[2]])
console.log(obj[keys[3]])
You can also construct a function that will return the value of a property by accepting two parameters: the object and the "index" (order position)
function getValue(obj, index) {
let keysArray = Object.keys(obj)
let key = keysArray[index]
return obj[key]
}
Usage example getValue(obj, 2)
Snippet
let obj = {a: 'dog', b: 'cat', c: 'mouse'}
function getValue(obj, index){
let keysArray = Object.keys(obj)
let key = keysArray[index]
return obj[key]
}
console.log(getValue(obj, 2))

JavaScript: Why does the "for in" loop pick variables from __proto__?

I added a method trigger to the prototype of Object:
Object.prototype.trigger = function() {
// ...
return this;
};
And then have a "for in" loop:
var obj = { 4: 15, 10 : 41, 11 : 46, 12 : 51, 20 : 74 }
for( item in obj ) {
foo( obj[item] );
}
But this loop has 6 iterations instead of 5. The last iteration is with key:
item = "trigger"
Why is the loop iterating over the __proto__ part of the object?
for...in goes over all of the object properties without distinguishing between properties on the object itself or any of its ancestors.
In order to go over only properties defined on the object itself, you can use Object.prototype.hasOwnProperty:
const obj = { 4: 15, 10 : 41, 11 : 46, 12 : 51, 20 : 74 }
for( item in obj ) {
if(obj.hasOwnProperty(item) {
foo( obj[item] );
}
}
// will ignore the trigger func and everything else defined on any prototype
for..in iterates over all enumerable properties anywhere on the prototype chain. If you want to make trigger non-enumerable so it isn't iterated over with for..in, use Object.defineProperty instead, which makes the defined property non-enumerable by default:
Object.defineProperty(Object.prototype, 'trigger', { value: function() {
}});
var obj = { 4: 15, 10 : 41 }
for( item in obj ) {
console.log(item);
}
Another way to do is to use Object.keys instead :
Object.prototype.trigger = function() {
return this;
};
var obj = { 4: 15, 10 : 41, 11 : 46, 12 : 51, 20 : 74 }
Object.keys(obj).forEach(function(item) {
console.log( "Key: " + item + ", value: " + obj[item] );
});
This was an error I faced with arrays some time ago. Because arrays are also objects and you could iterate down into the prototype, do not use for..in for the arrays. Your example is specific for an object that causes this issue. Instead, use standard for to iterate through the items of your object. That way you could have a cleaner and safer implementation for what you are trying to achieve, rather than using defineProperty or hasOwnProperty which are mostly used to implement reflection.
for (var i = 0; i < Object.values(obj).length; i++){
console.log(i + ': ' + Object.values(obj)[i]);
//foo( Object.values(obj)[i] );
}

indexOf but for objects? [duplicate]

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`

why inherited (have confusion if it is inherited at all) properties are being enumerated in javascript?

I knew that inherited properties are not enumerable. In the following code stanford inherits a property level from another object university.
But when I enumerate stanford, I see that the inherited property also being listed. Please explain the matter (i must be wrong somewhere).
var university = {level:"bachelor"};
var stanford = Object.create(university);
stanford.country = "USA";
stanford.rating = "Good";
console.log(Object.hasOwnProperty("level")); // false
for(var properties in stanford){ // level, country, rating
console.log(properties);
}
Such a wonderful link!! Thanks #Sajib Biswas for this question.
Refer object-getownpropertynames-vs-object-keys
and how-do-i-enumerate-the-properties-of-a-javascript-object
I think This may help :
var proto = Object.defineProperties({}, {
protoEnumTrue: { value: 1, enumerable: true },
protoEnumFalse: { value: 2, enumerable: false }
});
var obj = Object.create(proto, {
objEnumTrue: { value: 1, enumerable: true },
objEnumFalse: { value: 2, enumerable: false }
});
for (var x in obj) console.log(x);
Results:
objEnumTrue
protoEnumTrue
console.log(Object.keys(obj)); // ["objEnumTrue"]
console.log(Object.getOwnPropertyNames(obj)); // ["objEnumTrue", "objEnumFalse"]
The enumeration will return properties not just of the object being enumerated, but also from the prototypes of any parent objects.
Object.getOwnPropertyNames(a) returns all own properties of the object a.
Object.keys(a) returns all enumerable own properties.
From Your Example:
var university = {level:"bachelor"};
var stanford = Object.create(university);
stanford.country = "USA";
stanford.rating = "Good";
console.log(university.hasOwnProperty("level")); // true
console.log(university.hasOwnProperty("country")); // false
console.log(stanford.hasOwnProperty("country")); // true
console.log(stanford.hasOwnProperty("level")); // false
which properties are not enumerable?
Javascript has two types of object properties: Data properties and Accessor properties. In your case you are working with data properties.
To manipulate enumeration of certain property use property descriptor with Object.defineProperty() method.
Example:
var stanford = Object.create(university);
stanford.country = "USA";
stanford.rating = "Good";
Object.defineProperty(stanford, 'rating', {
enumerable: false
});
console.log(Object.keys(stanford)); // Now you won't see 'rating' here
// the same effect takes place for loop operators (for;;) , for .. in ..)
This is the structure of your stanford-object:
As you can see it inherits the other Object wich has the property level, but does not "hasOwnProperty" level. However for..in will loop over all properties.

jquery extend vs angular extend

What is the difference between these two extend functions?
angular.extend(a,b);
$.extend(a,b);
While the jquery.extend is well documented the angular.extend lacks details and the comments there provide no answers. (https://docs.angularjs.org/api/ng/function/angular.extend).
Does angular.extend also provide deep copy?
angular.extend and jQuery.extend are very similar. They both do a shallow property copy from one or more source objects to a destination object. So for instance:
var src = {foo: "bar", baz: {}};
var dst = {};
whatever.extend(dst, src);
console.log(dst.foo); // "bar"
console.log(dst.baz === src.baz); // "true", it's a shallow copy, both
// point to same object
angular.copy provides a deep copy:
var src = {foo: "bar", baz: {}};
var dst = angular.copy(src);
console.log(dst.baz === src.baz); // "false", it's a deep copy, they point
// to different objects.
Getting back to extend: I only see one significant difference, which is that jQuery's extend allows you to specify just one object, in which case jQuery itself is the target.
Things in common:
It's a shallow copy. So if src has a property p that refers to an object, dst will get a property p that refers to the same object (not a copy of the object).
They both return the destination object.
They both support multiple source objects.
They both do the multiple source objects in order, and so the last source object will "win" in case more than one source object has the same property name.
Test page: Live Copy | Live Source
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<meta charset=utf-8 />
<title>Extend!</title>
</head>
<body>
<script>
(function() {
"use strict";
var src1, src2, dst, rv;
src1 = {
a: "I'm a in src1",
b: {name: "I'm the name property in b"},
c: "I'm c in src1"
};
src2 = {
c: "I'm c in src2"
};
// Shallow copy test
dst = {};
angular.extend(dst, src1);
display("angular shallow copy? " + (dst.b === src1.b));
dst = {};
jQuery.extend(dst, src1);
display("jQuery shallow copy? " + (dst.b === src1.b));
$("<hr>").appendTo(document.body);
// Return value test
dst = {};
rv = angular.extend(dst, src1);
display("angular returns dst? " + (rv === dst));
dst = {};
rv = jQuery.extend(dst, src1);
display("jQuery returns dst? " + (rv === dst));
$("<hr>").appendTo(document.body);
// Multiple source test
dst = {};
rv = angular.extend(dst, src1, src2);
display("angular does multiple in order? " +
(dst.c === src2.c));
dst = {};
rv = jQuery.extend(dst, src1, src2);
display("jQuery does multiple in order? " +
(dst.c === src2.c));
function display(msg) {
$("<p>").html(String(msg)).appendTo(document.body);
}
})();
</script>
</body>
</html>
There is one subtle difference between the two which was not mentioned in previous answers.
jQuery's .extend() allows you to conditionally add key,value pairs, only if the value is defined. So in jQuery, this: $.extend({}, {'a': x ? x : undefined}); will return {} in case x is undefined.
In Angular's .extend() however, this: angular.extend({}, {'a': x ? x : undefined}); will return {'a': undefined}, even if x is undefined. So the key will be there, no matter what.
This could be a good or a bad thing, depending on what you need. Anyway this is a difference in behavior between the two libraries.
The 1.0.7 angularjs build states that the extend & copy methods no longer copy over the angularjs internal $$hashKey values.
See release notes # https://github.com/angular/angular.js/blob/master/CHANGELOG.md
angular.copy/angular.extend: do not copy $$hashKey in copy/extend functions. (6d0b325f, #1875)
A quick test of the angular.copy in Chomre dev tools method shows that it does do a deep copy.
x = {p: 3, y: {x: 5}}
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z = angular.copy(x);
Object {p: 3, y: Object}
z
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z.y.x = 1000
1000
x
Object {p: 3, y: Object}
p: 3
y: Object
x: 5
__proto__: Object
__proto__: Object
z
Object {p: 3, y: Object}
p: 3
y: Object
x: 1000
__proto__: Object
__proto__: Object
angular.extend on the other hand does a shallow copy.
.extend() in AngularJS works similarly to jQuery's .extend()
http://jsfiddle.net/Troop4Christ/sR3Nj/
var o1 = {
a: 1,
b: 2,
c: {
d:3,
e:4
}
},
o2 = {
b: {
f:{
g:5
}
}
};
console.log(angular.extend({}, o1, o2));
console.log(o1);
console.log(o2);

Categories

Resources