I have a JavaScript object that is treated as an associative array. Let's call it "fields". It has several elements, e.g.:
fields['element1'] = ...
fields['element2'] = ...
fields['element3'] = ...
Given fields[0], is it possible to obtain the name of the property (which is "element1") instead of its value?
Let's say you have an object oObject. It could be:
var oObject = {} ;
oObject["aaa"] = "AAA" ;
oObject["bbb"] = "BBB" ;
oObject["ccc"] = "CCC" ;
oObject["ddd"] = "DDD" ;
oObject["eee"] = "EEE" ;
Now, let's say you want to know its properties' names and values, to put into the variable strName and strValue. For that you use the "for(x in o)" construct, as in the following example:
var strName, strValue ;
for(strName in oObject)
{
strValue = oObject[strName] ;
alert("name : " + strName + " : value : " + strValue) ;
}
The "for(x in o)" construct will iterate over all properties of an object "o", and at each iteration, will put in variable "x" the current property name. All you have to do, then, to have its value, is to write o[x], but you already knew that.
Additional info
After some thinking, and after seeing the comment of Hank Gay, I feel additional info could be interesting.
Let's be naive (and forget the "in JavaScript, all objects, including arrays, are associative containers" thing).
You will usually need two kind of containers: Maps and Arrays.
Maps are created as in my example above (using the "o = new Object() ;" or the "o = {} ;" notation, and must be accessed through their properties. Of course, maps being maps, no ordering is guaranteed.
Arrays are created differently, and even if they can be accessed as maps, they should be accessed only through their indices, to be sure order is maintained.
Point is:
If you need a map, use a "new Object()" container
If you need an array, une an array, use a "new Array()" container
Don't EVER mix the two, and don't EVER access the map through indices, and for arrays, ALWAYS access its data through its indices, because if you don't follow those principles, you won't get what you want.
No, for two reasons.
fields[0] and fields["element1"] are different properties.
properties in an object are explicitly unordered
You could loop over the properties:
function (obj) {
for (prop in obj) {
if (obj.hasOwnProperty(prop) {
return prop;
}
}
};
…to get the "first" property for some arbitrary value of "first" that could change at any time.
http://ajaxian.com/archives/fun-with-browsers-for-in-loop explains the hasOwnProperty pattern.
There is no fields[0] (unless fields is an Array object, which supports numerical indices), so you can't get its name just like that. But you can simulate it like this:
function getKey(obj, i) {
var j = 0;
for (var p in obj) {
if (j++ == i) return p;
}
return null;
}
for (var p in obj) will loop through every field name in the object obj. By getting the nth field name, you can effectively get the "key" for a certain index.
Note that while it's working its way to become a standard, the order of field names is currently not guaranteed according to the standards, which means that after modifying the object, the same function call could theoretically return a different field name. Same thing goes that different browsers can return different results. Practically, you'll find that just about all the browsers do keep the order of field names so you shouldn't have to worry about it at all.
Just to point out what is implicit in everyone else's answer: "associative arrays" in Javascript are actually just Object instances, e.g.,
var aa = {};
aa.foo = 'argle';
alert(aa['foo']); // Will alert 'argle'
PLEASE don't use an Array instead of an Object—it has the potential to wreak havoc on for key in aa-style iteration.
Related
Why does this array have a length property of 0? And as a follow up to that question, why can't you access an item in this array with an index value?
Consider the following:
var box = [];
box['material'] = 'cardboard';
box['size'] = 'small';
box.length; //0
console.log(box);
//[ material: 'cardboard', size: 'small' ]
box[0]; //undefined
However, when I do the following:
var box = [];
box['0'] = true;
box['1'] = 'cardboard';
box['2'] = 'some value';
box.length; //3
box[0];// true
Why does it output the correct length in this case, since '0' is a string and not a number and access the correct item with the 0 index?
In JavaScript, the length of an array is always one more than the largest numeric (integer) property name. Arrays can have properties whose names are not numeric, but they don't count towards the length of the array (and they are ignored in some other important situations).
Object property names are always strings, but strings that are non-negative integer values when interpreted as numbers are special in arrays. Accessing properties with numeric values works because the numbers are first converted to strings; thus box[0] and box['0'] do exactly the same thing.
when setting box['material'] it creates a property called 'material' with value 'cardboard'. this doesn't add a element to the list!
you need to use .push() or asign the index you want.
also, the '0' string is cased to a number, so obj['0'] and obj[0] is the same (not just for 0, this happens for all numbers)
I think you’re confusing objects with arrays. While arrays are indeed objects in javascript, that doesn’t help us here.
What you are trying to instantiate is an associative array with key-value pairs “material”->“cardboard”, “size”->“small”. However, such a datastructure does not exist in javascript. Array must only have integers as indexes, as in
box = [];
box[0] = 'material';
box[1] = 'someValue';
The reason why your code (which only seems to be an associative array)
box = [];
box['0'] = true;
box['1'] = 'cardboard';
works is because of implicit type conversion. Because you’ve declared box to be an array, a string is not valid in this position, so javascript quietly converts it into an integer, so you again get a plain, boring, integer-indexed array as above.
What you can do, though, is create an object with attributes. So you might, for example, state
box = {}; // box is now an object
box.size = 'small'; // box now has an attribute called “size”
// which has the (string) value “'small'”
box.material = 'cardboard';
or at one go using the notation for object literals:
box = {
size: 'small',
material: 'cardboard'
};
This is the closest you can get to associative arrays in javascript. Note that objects do not have a length. If you want to access the number of attributes they have, you have to use Object.keys(box).length. To iterate over the keys, use
for(var key in box){
console.log('value of ' + key + ' is ' + box[key])
}
Note the array-like notation here, but remember: You’re dealing with objects, not arrays, though dealing with them looks similar. Since box is of type object, you will not only get the attributes you have defined yourself, but also any attributes which are defined for every object. To get only the keys you put in there yourself, use
for (var key in box) {
if (box.hasOwnProperty(key)) {
// do stuff
}
}
If you need to add keys, i.e. attributes at runtime, use (for example)
//supposing that box was instantiated as above
var listOfBoxAttributes = ['shape', 'color', 'weight'];
for(var i in listOfBoxAttributes)
box[listOfBoxAttributes[i]] = undefined;
Summary: Is there a faster way to hash objects than JSON.stringify?
Details: I have a Ruby and JavaScript library (NeatJSON) that provides pretty-printing of JavaScript values. I recently fixed a problem where deeply-nested objects caused O(n!) performance (n being the nesting level) using memoization based on the object being serialized and the indentation amount.
In Ruby, the fix was really easy, because you can index hashes by arrays of unique sets of objects:
build = ->(object,indent) do
memoizer[[object,indent]] ||= <all the rest of the code>
end
In JavaScript, however, I can't index an object by another object (in a unique way). Following the lead of several articles I found online, I decide to fix the problem generically, using JSON.stringify on the full set of arguments to the function to create a unique key for memoization:
function memoize(f){
var memo = {};
var slice = Array.prototype.slice;
return function(){
var args = slice.call(arguments);
var mkey = JSON.stringify(args);
if (!(mkey in memo)) memo[mkey] = f.apply(this,args);
return memo[mkey];
}
}
function rawBuild(o,indent){ .. }
var build = memoize(rawBuild);
This works, but (a) it's a little slower than I'd like, and (b) it seems wildly inefficient (and inelegant) to perform (naive) serialization of every object and value that I'm about to serialize smartly. The act of serializing a large object with many values is going to store a string and formatting result for EVERY unique value (not just leaf values) in the entire object.
Is there a modern JavaScript trick that would let me uniquely identify a value? For example, some way of accessing an internal ID, or otherwise associating complex objects with unique integers that takes O(1) time to find the identifier for a value?
If you are looking to memoise your objects by identity (not by content), then you'll want to use a WeakMap which is designed for exactly this purpose. They don't work for primitive values though, so you'll need a different solution for such arguments.
Using #Bergi's suggestion of a WeakMap I found out about Map, which allows using any value type as the key (not just objects). Because I needed a compound key—uniquely memoizing the combination of the value passed in and the indentation string—I created a hierarchical memoization structure:
function memoizedBuild(){
var memo = new Map;
return function(value,indent){
var byIndent=memo.get(value);
if (!byIndent) memo.set(value,byIndent={});
if (!byIndent[indent]) byIndent[indent] = rawBuild(value,indent);
return byIndent[indent];
}
}
This proved to be about 4× faster than the memoization code I had been using when serializing a large 270kB JSON object.
Note that in the above code I'm able to use !byIndent[indent] only because I know that rawBuild will never return a falsey value (null, undefined, false, NaN, 0, ""). The safer code line would look something like:
if (!(indent in byIndent)) byIndent[indent] = rawBuild(value,indent);
If you just need to memoise objects then it makes sense to assign some unique ID to your objects .
var gID = 0;
function createNode() {
var obj = ...
obj.id = (++gID).toString();
}
and use those obj.id's as keys in your memo collection.
That would be fastest and least greedy solution.
Update:
If you want that id property to do not clash with existing properties
then you can create non-enumerable properties using standard ES5.1 Object.createProperty() (with some unique name) or to use ES6 symbols:
var gID = 0;
var gUidSym = Symbol("uid");
function getUidOf(obj) {
return obj[gUidSym]
|| (obj[gUidSym] = (++gID).toString());
}
In Javascript, I have an array of objects, users, such that users[1].name would give me the name of that user.
I want to use the ID of that user as the index instead of the ever increasing counter.
For example, I can initiate the first user as users[45].
However, I found that once I do users[45], javascript would turn it into a numeric array, such that when I do users.length, I get 46.
Is there anyway to force it to treat the number as string in this case. (" " doesn't work)?
You cannot use arrays for this sort of function in JavaScript — for more information, see "Javascript Does Not Support Associative Arrays."
Make sure you initialize the users variable as an Object instead. In this object you can store the arbitrary, non-sequential keys.
var users = new Object();
// or, more conveniently:
var users = {};
users[45] = { name: 'John Doe' };
To get the number of users in the object, here's a function stolen from this SO answer:
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
var users = {};
// add users..
alert(Object.size(users));
Hans has the right answer here. Use keys on an object, not an array. I'd suggest you read these two references:
http://www.quirksmode.org/js/associative.html and
http://blog.xkoder.com/2008/07/10/javascript-associative-arrays-demystified/
Trying to make an associative array out of an array object is non-standard and can cause problems (for example .length will be zero). Use keys on an object instead.
As per what I know, arrays in Javascript is nothing but the combination of methods and objects.
Now my task is to display the values of array (say y_array)
I have used for(x in y_array) and then displayed the value.
In mozilla and in IE its working fine, but in IE it displays the first element of array with index as indexOf and value is indexOf(obj, from) which i dont want.
I tried
if(x!='indexOf') { display the array value ; }
It worked and things were fine but there is extensive use of arrays been displayed and I am looking for some permanent fix rather than this hardcoded one.
Can anyone please help me?
You are not the first mixing up arrays and objects. SO should contain a FAQ for this kind of questions ;)
Let's try to explain things:
An array is a row of values, which can be retrieved using their position in the row. The order of the array values is fixed (and may be reordered).
An object is a variable that contains named properties in the form of key-value pairs. The order of the key-value pairs belonging to an object is arbitrary.
An array looks like: [ 'first', 'second', 'third', ..., 'nth' ]
An object looks like: { first:'firstvalue', second:'secondvalue', ..., nth:'nthvalue' }
The first element of an array is the element with index 0 (so the first position in the row has index value 0). You retrieve it using myArray[0]
An object is unordered, so it has no first element. You retrieve any element from it using myObject.somekey or myObject['somekey'].
For arrays you use a loop iterating through the numbered index until the end of the array is reached:
var i=0, len = myArray.length;
for ( i; i<len; i++ ) {
//do something with >>> myArray[i] <<<
}
For objects you use a loop using the key and the in operator (making sure you are only retrieving user defined properties of the object with the .hasOwnAttribute method):
for ( var key in myObject ){
if (myObject.hasOwnProperty(key)) {
// do something with >>> myObject[key] <<<
}
}
Basically, think of an array as a cupboard with drawers, each containing a value. An object can be imagined as a pile of boxes with stickers on the lid, describing the content of the box. Retrieving something from an object, you ask: is there a box with sticker y in pile x and if so, what's in it? Retrieving something from an array, you ask: please give me the contents of drawer nr x.
Now as to your question: the array you are retrieving values for with a for..in loop contains a user defined method, namely indexOf. Using the object style loop for it, the array is treated as object, and the indexOf key (with value like function(){...} I bet) is shown too. IE That's why it may be better to use a traditional for loop with a numeric index when iterating over arrays.
Why is this only in IE? In modern browsers indexOf is a native method of the Array prototype, and native methods are not shown (unless you loop through their prototype that is). IE < 9 doesn't have a native indexOf method for arrays. Somewhere in the scripting you use the method has been added to the Array prototype as a user defined extension.
Bottom line for your problem: don't use for ... in to loop through the values of an array.
For arrays you should use this for loop:
var y_array = [1,2,3,4];
for (var i = 0; i < y_array.length; i++) {
var value = y_array[i];
// do what you want
alert(i + ': ' + value);
}
For objects (objects are like associative arrays - property: value) use this loop:
var y_array = { prop_1 : "value a", prop_2: "value_2", prop_3: 333 }
for (var key in y_array) {
var value = y_array[key];
// do what you want
alert(key + ': ' + value);
}
if there is no value in your json Object like jsobObj = {}. Then you got the indexOf prototype function in side the empty object in IE < 9. (with value like function(){...} I bet) is shown too.
Your can check a condition in side your for Loop. and skip that indexOf.
if(key =='indexOf'){continue;}
E.g :
var jsonObj = { key_1 : "value a", key_2: "value_2", key_3: 333 }
for (var key in y_array) {
if(key == 'indexOf'){continue;} // check if the array contain indexOf
var value = y_array[key];
// do what you want
alert(key + ': ' + value);
}
In the following code why the variable 'a' refer to the index rather than the value ?
for (var a in Values) {
alert(Values[a]);
}
That's by design. It's trivial to get a value in an array when you know its key, but it's much harder to get a key given a value. Values can be duplicated, so how do you know which key should be used? But a key's unique, so given a key, there's only ever one value to retrieve. So, the for loop will iterate over the keys, and it's trivial to get the associated value.
Think of a JavaScript Array as a normal Object with a special property named length (actually, it a bit more complex). So the for..in loop behaviour is identical as for other objects:
var a = new Array();
a[1] = "a";
alert(a.length); // 2
alert(a[0]); // undefined
a[1000] = "b"
alert(a.length); // 1001
a[-1] = "c";
alert(a[-1]); // c
a.abc="why not";
for(var key in a)
{
alert(key+"="+a[key]);
}
// 1=a
// 1000=b
// -1=c
// abc=why not
Also note that you can have gaps within your array without having to pay the memory price.
There is a for each...in loop that does exactly that - enumerates only values. Coming soon to a browser near you.
for each(var a in Values) {
..
}
For arrays, there is a new function forEach which achieves the same.
someArray.forEach(function(value) {
..
});