Using object as ES2015 map key - javascript

I've been trying to get a hold of ES2015 map concept and one thing I don't understand is the following:
var mapRawObj = new Map();
var rawObj = {j:"I like penguin"};
mapRawObj.set(rawObj,true);
console.log(mapRawObj.get(rawObj)); //this outputs true
mapRawObj.set({j:"I like polar bear"},true);
console.log(mapRawObj.get({j:"I like polar bear"})); //this outputs undefined
The first one works, the second one doesn't, and I don't understand why?
I thought when you register object as key, it's the object itself, not the object's name. That's why in the below example when you re-assign the key object, it fails as key?
var obj = { a:"hello ", b:"world "};
var mapObj = new Map();
mapObj.set(obj,true);
obj = {d:34}; //obj is re-assigned
console.log(mapObj.get(obj)); // outputs undefined

Objects with the same data are not equal in Javascript, ie
{ hello: 'world'} === { hello: 'world'} // false
The first example uses the same object as the key for set and get, so the key is identical:
var obj = { hello: 'world'};
obj === obj // true
But the second example creates a new object for the get(), which is not identical to the key used to set the value in the map. Since it's not identical, the map doesn't have anything set against this new key and returns undefined.
Even though the new key has exactly the same data as the original key, the objects aren't referring to the same data internally.
More on object equality

Related

Manipulating object in array changes object outside of array?

I don't understand why this behavior is happening. Lets say I define an object and make an array of 3 of this object. If I modify the objects in the array, it affects all instances of the object? Could someone explain why this is? Also, how do I make an array with independent "Copies" of the object to get the desired behavior? Thanks!
example
testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
var newobj = testObject; //make a new testObject
objArray.push(newobj); //push new object to array
}
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2
testObject.value2 //Undefined? Why is value2 missing from original object?
As opposed to primitives (strings, numbers, booleans, symbols null, undefined), objects in javascript are passed by reference. Variables serve as placeholders/pointers to these objects. To create a copy of an object without the risk of mutation you'd use spread (barring compatibility):
const newObject = { ...testObject };
or traditionally, Object.assign(), passing an empty object literal to avoid mutability of the original testObject:
const newObject = Object.assign({}, testObject);
As far as deep cloning, MDN suggests using a combination of JSON.parse() and JSON.stringify(). So for example:
const testObject = { value: "a", other: { value2: b } };
const newObject = JSON.parse(JSON.stringify(testObject));
You are pushing same object's reference again and again in the loop.
for(i=0; i < 3; i++){
var newobj = testObject; //no new object,same object's reference again
objArray.push(newobj); //push new object to array
}
It should be
for(i=0; i < 3; i++){
var newobj = {"value1":"a","value2":"b"}; //make a new testObject
objArray.push(newobj); //push new object to array
}
When creating an Object in JavaScript, you are actually creating a reference to that object. You can store this in a variable and pass it around ... perhaps append it to an array. When you go to do operations on an object reference, it finds the original object that the reference points to and updates it. Thus when you use .push it's not creating a new object but simply pushing the reference to that object. If you update it in one spot it will update it in the other and any others where you have assigned that reference.
Copying an object into a new object is generally called cloning. There are a lot of different ways to clone objects in JavaScript with varying results.
You can use var newobj = { ...testObject } as the other answer suggests. This spread operator essentially copies the properties of testObject and creates a new object (declared with the outer { }). The reference to that new object is then assigned to newobj. You can think of it as doing this:
var newobj = {
value1: testObject.value1,
value2: testObject.value2,
};
However, you should keep in mind that this gives you only one level of cloning. That is to say if your object contains other objects then the reference to that object will be assigned as the property rather than a clone of that object. For example: let's say you had:
var testObject = { obj: { a: "b" } };
var newobj = { ...testObject };
delete testObject.obj.a;
console.log(newobj); // { obj: {} }
In order to solve this, you need to do what is called a deep clone which in JavaScript can be done by recursively cloning object properties that are also objects. There are a bunch of ways to do this including libraries like lodash or home-grown functions. One example on SO: What is the most efficient way to deep clone an object in JavaScript?
Finally, if testObject is supposed to be something like an object template or initial state from which other newobj are derived, it might make more sense to use a function:
function newObjFactory() {
return {
value1: "a",
value2: "b",
};
}
Then you can do var newobj = newObjFactory() and you'll get a new object each time since a new object is created by the function each time it's called and returned.

Javascript reserved word and object

I'm making a dictionary of words, so there are 1,000,000+ words.
The problem comes when I need to store the word constructor. I know this is a reserved word in javascript, but I need to add it to the dictionary.
var dictionary = {}
console.log(dictionary ['word_1'])
//undefined, this is good
console.log(dictionary ['word_2'])
//undefined, this is good
console.log(dictionary ['constructor'])
//[Function: Object]
// this cause initialization code to break
How can I fix this? I could muck with the it like key=key+"_" but that seems bad. Is there anything else I can do?
Instead of using a JS object, you could use the built-in Map type which uses strings/symbols as keys and does not conflict with any existing properties.
Replace
var dictionary = {} with var dictionary = new Map()
Override the constructor key as undefined
According to the MDN Object.prototype page, the only thing that isn't hidden by the __fieldname__ schema is the "constructor field". Thus, you could just initialize your objects via { 'constructor': undefined }.
However, you would have to make sure that in your for .. in statements would filter out all keys with undefined as their value, as it would pick up constructor as a "valid" key (even though it wouldn't before you specifically set it to undefined). I.E.
for(var key in obj) if(obj[key] !== undefined) { /* do things */ }
Check for types when getting/setting
Otherwise, you could just check the type when you 'fetch' or 'store' it. I.E.
function get(obj, key) {
if(typeof obj[key] !== 'function') // optionally, `&& typeof obj[key] !== 'object')`
return obj[key];
else
return undefined;
}
I think you should store all words and translation of them in an array. When you need to translate a word, you can use find method of Array.
For example:
var dict = [
{ word: "abc", translated: "xyz" },
...
];
Then:
var searching_word = "abc";
var translation = dict.find(function (item) {
return item.word == searching_word;
});
console.log(translation.translated);
// --> xyz
To achieve expected result , use below option of using index to get value of any key value
var dictionary = {};
var dictionary1 = {
constructor: "test"
};
//simple function to get key value using index
function getVal(obj, val) {
var keys = Object.keys(obj);
var index = keys.indexOf(val);//get index of key, in our case -contructor
return obj[keys[index]]; // return value using indec of that key
}
console.log(getVal(dictionary, "constructor"));//undefined as expected
console.log(getVal(dictionary1, "constructor"));//test
console.log(dictionary["word_1"]);
//undefined, this is good
console.log(dictionary["word_2"]);
//undefined, this is good
codepen - https://codepen.io/nagasai/pen/LOEGxM
For testing , I gave one object with key-constructor and other object without constructor.
Basically I am getting the index of key first and getting value using index

JavaScript drops Array Brackets in Object Key value pair

Hey guys I noticed something strange today with trying to set a key value pair in JavaScript. I know that The Key of an object is always 'stringified' so that the key value pair is always string: value, however something strange happened today when I tried this with an array. Example below:
var ob = {};
var a = [2,4];
ob[a] = 10;
console.log("this is ob ", ob);
Here I have tried adding the key [2,4] to map to 10. However, the console log returns the string 2,4 mapping to 10 instead of the string [2,4] mapping to 10. Does anyone know why this happens?
Using an object or an array as the property name, doesn't invoke JSON#stringify, but the object's toString method, which in arrays returns the array elements joined by a comma.
In the example I override the Array#toString method, and you can see that the resulting property name reflects that:
var ob = {};
var a = [2,4];
a.toString = function() { return 'cats' }; // override toString
ob[a] = 10;
console.log("this is ob ", ob);

Using Javascript object as object key

I am trying to devise a way to use a simple Javascript object (one level deep key value pairs) as a key to another object. I am aware that merely using the object without stringification will result in [Object object] being used as the key; see the following: Using an object as a property key in JavaScript (so this question is not a duplicate).
There is a blog post about it that takes this into account and also accounts for the need to sort by object key since their order is not guaranteed, but the included Javascript code runs over a 100 lines. We are using the underscore.js library since that goes hand in hand with backbone but pure Javascript alternatives will also be of interest.
In ECMAScript 6 you'll be able to use Maps.
var map = new Map();
var keyObj = { a: "b" },
keyFunc = function(){},
keyString = "foobar";
// setting the values
map.set(keyObj, "value associated with keyObj");
map.set(keyFunc, "value associated with keyFunc");
map.set(keyString, "value associated with 'foobar'");
console.log(map.size); // 3
// getting the values
console.log(map.get(keyObj)); // "value associated with keyObj"
console.log(map.get(keyFunc)); // "value associated with keyFunc"
console.log(map.get(keyString)); // "value associated with 'a string'"
console.log(map.get({ a: "b" })); // undefined, because keyObj !== { a: "b" }
console.log(map.get(function(){})); // undefined, because keyFunc !== function(){}
console.log(map.get("foobar")); // "value associated with 'foobar'"
// because keyString === 'foobar'
I wrote a hash table implementation that accepts arbitrary keys but I suspect you'll reject it on the grounds of the relatively large file size.
https://code.google.com/p/jshashtable/
Here is an underscore based solution that relies on first converting the object to key-value pairs.
var myObj = { name: 'john', state: 'ny', age: 12};
var objPairs = _.pairs(myObj);
var sortedPairs = _.reduce(_.keys(myObj).sort(), function(sortedPairs, key) {
var pair = _.find(objPairs, function(kvPair) {return kvPair[0] == key});
sortedPairs.push(pair);
return sortedPairs;
}, []);
console.log(JSON.stringify(sortedPairs)); //stringifying makes suitable as object key
// [["age",12],["name","john"],["state","ny"]]
You could use a pattern like this. This way, your key for an object is this random id that you generate for every object.
var MyObject = function(name) {
this.name = name;
this.id = Math.random().toString(36).slice(2);
}
MyObject.prototype.toString = function() {
return this.id;
}

JavaScript array of pointers like in C++

I'm faced with a situation in JavaScript when I need to update an object via its pointer similar to ะก++ array of pointers to objects
Example code for my issue:
var foo = new Array();
var bar = function(){
this.test = 1;
foo.push(this); // push an object (or a copy of object?) but not pointer
};
var barInst = new bar(); // create new instance
// foo[0].test equals 1
barInst.test = 2;
// now barInst.test equals 2 but
// foo[0].test still equals 1 but 2 is needed
So, how can I solve this? Should I use a callback or something like this or there is an easy way to help me to avoid copying the object instead pushing the raw pointer into an array?
JS is pass-by-value, so your original assignment was this.test = the value of 1, in my example, it's this.test = the object pointed to by ptr, so when I change ptr this.test changes as well.
var foo = [],
ptr = {val: 1},
bar = function(){
this.test = ptr;
foo.push(this); // push an object (or a copy of object?) but not pointer
},
barInst = new bar(); // create new instance
// foo[0].test.val equals 1
ptr.val = 2;
// foo[0].test.val equals 2
Although if you thought that foo.push(this); was similar, it isn't. Since this is an object, the array will indeed contain "raw pointers" to objects, just like you want. You can prove this simply:
foo[0].test = 3;
// barInst.test === 3
Which shows that it is indeed a pointer to the object that was pushed onto the array
"create object method pointer"
Object.defineProperty(Object.prototype,'pointer',{
value:function(arr, val){
return eval(
"this['"+arr.join("']['")+"']"+
((val!==undefined)?("="+JSON.stringify(val)):"")
);
}
});
ex of use
var o={a:1,b:{b1:2,b2:3},c:[1,2,3]}, arr=['b','b2']
o.pointer(arr) // value 3
o.pointer(['c',0], "new_value" )

Categories

Resources