Understanding how javascript hashtables work - javascript

Could anyone explain to me why the code sample below reports true? I would have assumed that like in C# the instance of Test1 != instance of Test2.
Update: So I think I will go with some unique identifier stored in the base of both Test1 and Test2.
function Test1() { };
function Test2() { };
var test1 = new Test1();
var test2 = new Test2();
var dict = new Array();
dict[test1] = true;
alert(dict[test2]);

Your object (JavaScript's hashtable) is not using the instance of test1 or test2, but the string representation, as a key. Since both test1 and test2 have the same string representation: "[object Object]", the true value is associated with that key.
Try doing something like below instead:
function Test1(id) { this.id=id };
function Test2(id) { this.id=id };
var test1 = new Test1('1');
var test2 = new Test2('2');
var dict = {};
dict[test1.id] = true;
console.log(dict[test1.id]);

Keys in 'hashtables' (objects, basically) are always strings. So anything you add will be converted to a string.
new Test1();
returns a instance of Test1. Converted as a string, this is:
"[object Object]"
The same goes for Test2. So in fact, when storing true under the key of new Test1() as a string, you are working with the exact same record as the one by obtaining with the key new Test2() as a string. In other words,
(new Test1()).toString() == (new Test2()).toString();
The actual object is therefore simply:
{
"[object Object]": true
}
A solution is overwriting .toString() like this:
Test1.prototype.toString = function() { return "Test1" };
Test2.prototype.toString = function() { return "Test2" };
Then dict[test1] will be stored as dict['Test1'] and dict[test2] as dict['Test2'], which allows you to differ between them. Still, manually setting dict['Test1'] would overwrite things. As far as I know, there is no way to assign an object as a key.

Javascript objects aren't exactly hashtables; they're actually objects with string keys.
When you use an object as a key, the object is converted to a string by calling toString().
toString() will return the same string for all custom classes (unless you create your own toString), so they end up using the same key.

First: Use arrays only for numerical keys. For anything else use objects.
Property names can only be strings. Anything else is converted to its string representation. In case of objects, this is [object Object] or whatever toString() returns.
Which means, that if you want to make both objects distinguishable, you have to override this method and let it return something which is unique to each instance.
This question might help you: Hash/associative array using several objects as key

Related

Can you create object property names using template literals in javascript?

I'm attempting to write a function that takes an array of Strings as its input. The function should return an object with (1) key, value pair. The first element of the array should be the property name and the last element of the array should be its key.
function transformFirstAndLast(array){
return {`${array[0]}`: array[length-1];}
}
The above gives me an error. Can someone provide a detailed explanation why this isn't working? I'd like to avoid creating separate variables to store the values from the first and last array indices.
Your question really boils down to, "Can I use expressions as keys in object literals?"
The answer is yes (since es6):
function yell(template, ...parts) {
return parts[0] + '!';
}
function foo() {
return 'bar';
}
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
toString() {
return `${this.first} ${this.last}`;
}
}
let names = ['Abe'];
let my_obj = {
[3+5]: 'some_value',
[yell `${foo()}`]: foo(),
[names[0]]: 64,
[new Person('Rafael', 'Cepeda')]: 25
};
console.log(my_obj);
As long as the expression evaluates to a string, all is fair.
You are missing the { and you can't use a template strings as a key. To use a "variable" as a key in an object, you should use the brakets around the variable. Here's the working code.
function transformFirstAndLast(array){
return {[array[0]]: array[array.length-1]};
}
I guess template literal is unnecessary here simply try like this way after fixing this line array[length-1] because its wrong, correct one is array[array.length-1]. I've added more verbose code but you can also do the shorthand version like return {[array[0]]: array[array.length-1]}; on your transformFirstAndLast(array) function.
function transformFirstAndLast(array){
const result = {};
result[array[0]] = array[array.length-1];
return result;
}
console.log(transformFirstAndLast(['a','b','c','d','e']))
yes you can use string literals as key. But the thing is that they are calculated in the run time. So you need to treat them as expressions. And to have variables/expressions as your keys you need to wrap them inside []
let a = {
`key`: value
} // is not allowed
let a = {
[`key`]: value
} // is allowed since you have wrapp
for your case
return {[`${array[0]}`]: array[array.length-1]};
Now since you have wrapped the array[0] item inside a string literal, you will get string values for your zero'th item. If your array[0] is to be a object this would not work as well. It needs to be either string or number. Or else you would get "[object Object]" as your key
var input = ["name", "fname", "lname", "stackOverflow"];
function transformFirstAndLast(array){
return {[array[0]]: array.pop()}
}
responseObject = transformFirstAndLast(input)
console.log(responseObject)

Object.prototype.toString() - trying to get 'String' instead of [object String]

I am trying to get the type of an argument by not using the typeof. Also (its part of an exercise) I must use Object.prototype.toString...
Calling the function should return (for example if its a string) "String". However, my code returns String]
How can I remove the bracket?
Thanks!
function types(x){
var array = Object.prototype.toString.call(x);
var arr= array.split(" ");
return arr[1];
}
types("hello");
I don't know why you have to use Object.prototype.toString as native string is not an object. If you force it on a string, then it will return the "[object String]".
If you want the type of a variable, then you can simply use the following function
var types = x => x.contructor.name;
If you must, then you use the following
var types = x => Object.prototype.toString.call(x).match(/\[object (.*)\]/)[1];
This is inefficient as you first wrap the original variable with an object variable, then call its toString function, followed by a regExp (or substr).
This will return String. Uses slice() to remove the trailing ]
function types(x) {
var array = Object.prototype.toString.call(x);
var arr = array.split(" ");
return arr[1].slice(0, -1);
}
console.log(types('hello')) // String

In Javascript, why does array.map(String) return array of strings?

The list processing routine map on an array object is very convenient at times. Here's one of the handy ways to use it:
var numarr = [1,2,3,4];
console.log(numarr.map(String))
>>> ["1", "2", "3", "4"]
I took this for granted thus far. Today I was however puzzled by it. What the map function is returning above is an array of strings. We typically pass a function to map as argument. In above case we pass String object. String is implemented inside the Javascript implementation, so I don't know what kind of specialities it has. The above code works as if a new instance of String is created for each item in array.
If it's not clear, consider this. If I decide to implement an object in Javascript say MyString and pass it to map, I won't get the above behavior.
function MyString(x) { this.val = x; }
MyString.prototype.toString = function () { return String(this.val); };
var ms = new MyString(4)
console.log(String(ms));
>>> "4"
var arr = [1,2,3];
arr.map(MyString)
>>> [undefined, undefined, undefined]
Does anyone know why then arr.map(String) works the way it does?
Update: A comment I added below clarifies my question better.
At the end of the 2nd snippet, try console.log(val). You'll notice you've leaked a global:
var arr = [1,2,3];
arr.map(MyString);
console.log(val); // "3"
When using arr.map(MyString), you're calling that constructor as a function, without the new to create instances. And, since MyString doesn't return anything, you get undefined in the results. But, you've still set this.val, while this isn't an instance but is rather the global object.
String doesn't return undefined because it has a return when called without new:
When String is called as a function rather than as a constructor, it performs a type conversion.
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty String "" is returned.
You can imitate this with MyString by checking if this is an instance first, returning a new instance when this isn't one already:
function MyString(x) {
if (this instanceof MyString) {
this.val = x;
} else {
return new MyString(x);
}
}
var arr = [1, 2, 3];
arr.map(MyString); // [ {val: "1"}, {val: "2"}, {val: "3"} ]
Array.map returns an array whose elements are the value returned by applying the specified function to each value in the this array. String is a function; it returns a string. That's all there is to it.
Thats is because String is a function. It returns a string constructed from what is passed to it. For example, if you call String(100), it will return "100".

How to create a 'real' JavaScript array in Rhino

Okay, I'm a little stumped. I'm probably missing something blatantly obvious but apparently I just can't see the forest for the trees:
I'm trying to call a JavaScript function that expects its parameter to be an array, i.e. it checks if (arg instanceof Array)... Unfortunately, I (or Rhino) just can't seem to create such an array:
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
String src = "function f(a) { return a instanceof Array; };";
cx.evaluateString(scope, src, "<src>", 0, null);
Function f = (Function) scope.get("f", scope);
Object[] fArgs = new Object[]{ new NativeArray(0) };
Object result = f.call(cx, scope, scope, fArgs);
System.out.println(Context.toString(result));
Context.exit();
And alas, result is false.
What am I missing here?
Edit:
Just a little more information: both [] instanceof Array and new Array() instanceof Array return true as one would expect. If I add elements to the array they show up in the JavaScript code with the right indices (numeric, starting from zero):
NativeArray a = new NativeArray(new Object[]{ 42, "foo" });
When output using this JavaScript function:
function f(a) {
var result = [];
result.push(typeof a);
for (var i in a) {
result.push(i + ' => ' + a[i]);
}
return result.join('\\n');
}
The result is:
object
0 => 42
1 => foo
So it works. Except that I want a 'real' array :)
Almost forgot: Object.prototype.toString.call(a) returns [object Array]
Okay, that's the crucial information. That tells us that the array really is an array, it's just that it's being initialized by an Array constructor in a different scope than the one that the function is testing for, exactly as though you were testing an array from one window against another window's Array constructor in a browser-based app. E.g., there's a scope problem.
Try replacing
Object[] fArgs = new Object[]{ new NativeArray(0) };
with
Object[] fArgs = new Object[]{ cx.newArray(scope, 0) };
...to ensure the correct Array constructor is used. Because you've gone directly to the NativeArray constructor, you've bypassed ensuring that its scope is right, and so the array object's constructor is an Array constructor, but not the same Array constructor as the one on the global object the function sees.
For those who are intentionally creating a different subclass of the array implementation, and therefore can't use cx.newArray, what you can do is:
add this line
ScriptRuntime.setBuiltinProtoAndParent(fArgs, scope, TopLevel.Builtins.Array);

How to access the properties of a JavaScript object?

while review a javascript coding, i saw that
var detailInf = {
"hTitle":"Results",
"hMark":"98"
};
What's the concept behind this js coding. While give alert for the variable its shows as "[object Object]". So this is an object, then how can we access the variable and reveal the data from this object.
Try doing this:
alert(detailInf['hTitle']);
alert(detailInf.hTitle);
Both will alert "Results" - this is a Javascript object that can be used as a dictionary of sorts.
Required reading: Objects as associative arrays
As a footnote, you should really get Firebug when messing around with Javascript. You could then just console.log(detailInf); and you would get a nicely mapped out display of the object in the console.
That form of a JavaScript object is called an object literal, just like there are array literals. For example, the following two array declarations are identical:
var a = [1, 2, 3]; // array literal
var b = new Array(1, 2, 3); // using the Array constructor
Just as above, an object may be declared in multiple ways. One of them is object literal in which you declare the properties along with the object:
var o = {property: "value"}; // object literal
Is equivalent to:
var o = new Object; // using the Object constructor
o.property = "value";
Objects may also be created from constructor functions. Like so:
var Foo = function() {
this.property = "value";
};
var o = new Foo;
Adding methods
As I said in a comment a few moments ago, this form of declaring a JavaScript object is not a JSON format. JSON is a data format and does not allow functions as values. That means the following is a valid JavaScript object literal, but not a valid JSON format:
var user = {
age : 16,
// this is a method
isAdult : function() {
// the object is referenced by the special variable: this
return this.age >= 18;
}
};
Also, the name of the properties need not be enclosed inside quotes. This is however required in JSON. In JavaScript we enclose them in brackets where the property name is a reserved word, like class, while and others. So the following are also equivalent:
var o = {
property : "value",
};
var o = {
"property" : "value",
};
Further more, the keys may also be numbers:
var a = {
0 : "foo",
1 : "bar",
2 : "abz"
};
alert(a[1]); // bar
Array-like objects
Now, if the above object would have also a length property, it will be an array like object:
var arrayLike = {
0 : "foo",
1 : "bar",
2 : "baz",
length : 3
};
Array-like means it can be easily iterated with normal iteration constructs (for, while). However, you cannot apply array methods on it. Like array.slice(). But this is another topic.
Square Bracket Notation
As Paolo Bergantino already said, you may access an object's properties using both the dot notation, as well as the square bracket notation. For example:
var o = {
property : "value"
};
o.property;
o["property"];
When would you want to use one over the other? People use square bracket notation when the property names is dynamically determined, like so:
var getProperty = function(object, property) {
return object[property];
};
Or when the property name is a JavaScript reserved word, for example while.
object["while"];
object.while; // error
That's an object in JSON format. That's a javascript object literal. Basically, the bits to the left of the :'s are the property names, and the bits to the right are the property values. So, what you have there is a variable called detailInf, that has two properties, hTitle and hMark. hTitle's value is Results, hMark's value is 98.
var detailInf = { "hTitle":"Results", "hMark":"98"};
alert(detailInf.hTitle); //should alert "Results"
alert(detailInf.hMark); //should alert "98
Edit Paolo's answer is better :-)
As Dan F says, that is an object in JSON format. To loop through all the properties of an object you can do:
for (var i in foo) {
alert('foo[' + i + ']: ' + foo[i]);
}

Categories

Resources