Currently, I am creating a 3d array in js using the following:
var arr = [["name1", "place1", "data1"],
["name2", "place2", "data2"],
["name3", "place3", "data3"]];
I can access each element using arr[0] or arr[1]. But is there anyways I can access them using a key like this: arr["name1"] should give me the first one. Any suggestions? I think I am looking for a Hashmap like functionality.
The situation has changed in the six years since this question was asked.
Due to weak typing associative arrays can be faked in JavaScript:
>> var names = new Array();
undefined
>> names["first"] = "Dotan";
"Dotan"
>> names["last"] = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
That is sometimes useful, and all browsers released since 2012 support it, but there are caveats! The array cannot be simply read back:
>> names
Array [ ]
More importantly, the array's length cannot be easily retrieved:
>> names.length
0
Therefore this is not an associative array in the sense that JavaScript would have supported it had it been intended, but rather a workaround that is often useful if for whatever reason a real JS object does not support what you need:
>> var names = {};
undefined
>> names.first = "Dotan";
"Dotan"
>> names.last = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
>> names
Object { first: "Dotan", last: "Cohen" }
>> Object.keys(names).length
2
The only way you could do that is by wrapping it in an object.
var arr = {
name1 : ["name1", "place1", "data1"],
name2 : ["name2", "place2", "data2"],
name3 : ["name3", "place3", "data3"]
};
Javascript is a prototype based dynamic language. You can create objects and change their structure when you want.
var o = {name1: {place1: data1}, name2: {place2: data2}};
and access it with:
o.name1
The look-up implementation varies though and when you have a lot of properties that often changes this can be pretty slow (except in Chrome that uses a special scheme to access object properties i.e. embedded classes a-la-dynamic dispatch from smalltalk). Some libraries (e.g. MooTools) provide some hash map related structures,
Related
I have an object ( array of hashes ) and I need to convert it into ordered key value pairs.
This is object:
var places_info = [
{"place_id":180,"name":"Abc","city":"Gotham"},
{"place_id":161,"name":"Def","city":"Sin City"},
{"place_id":178,"name":"Ghi","city":"Timbuktu"},
{"place_id":179,"name":"Jkl","city":"Acapulco"},
{"place_id":174,"name":"Mno","city":"Desire"}
];
And I need function to return key/value (place_id/name) pairs in same (alphabetical by name) order:
{
'180': 'Abc',
'161': 'Def',
'178': 'Ghi',
'179': 'Jkl',
'174': 'Mno'
}
Actual use: I need it as input for Jeditable Select-input (actual example on demo page). Select-type needs for data-property key/value pairs or function which returns them. So far I got select options in random order, but--as you may imagine--I need them ordered by value....
As for Jeditable the data may be just a fake hash (string formed as JSON key/value pairs), my only solution is like this:
var arr = new Array;
for ( i in places_info ) {
arr.push( "'" + places_info[i].place_id + "':'" + places_info[i].name + "'" );
}
var uglyString = '{' + arr.join(',') + '}';
Is there some more elegant and intuitive approach for such goal? I'd like to keep my data in proper data structure. Converting it to flat string seems like hack.
Edit
I must reconsider the problem. I did not test Jeditable with generated string and seems it evals string into hash and then my desired order is gone. So, this problem needs different approach. Maybe I need make my own datatype for Jeditable. But this seems like different question.
Until ES6, there is NO guaranteed order for properties on a Javascript object. They are unordered. If you need order and need to support environments before ES6, then you can't just use properties on an object and get a guaranteed order of those properties.
So your choice is to pick some other data structure that can reliably express order. Collapsing it all down to a string like you've done is one choice, though that means it has to be re-parsed in order to be useful to Javascript. It would be better to keep it in some natively accessible Javascript format. For order, that means using an array.
You can return ordered pairs by returning an array for the ordering and then there are several choices for what to put in the array. Here are several examples of ordered key/value pairs:
// array of objects
var a = [{key: "greeting", value: "hello"}, {key: "salutation", value: "welcome"}];
Since that seems a little verbose to me, I often use a shortcut that just knows that every other element is a key, then value, then key, then value and doesn't have to spell out those property names over and over:
// alternating key/value
var a = ["greeting", "hello", "salutation", "welcome"];
Or, you could put each ordered pair in an array itself so each sub-array is an ordered pair:
// sub-arrays
var a = [["greeting", "hello"], ["salutation", "welcome"]];
The properties order in objects are not guaranted in JavaScript, you need to use an Array.
Take a look at this:
var obj = {
'180': 'Abc',
'161': 'Def',
'178': 'Ghi',
'179': 'Jkl',
'174': 'Mno'
};
for (var i in obj) { console.log(i); };
will give you:
161, 174, 178, 179, 180
Just use JavaScript's native Map data structure. It preserves order. We are now way past 2015 so it should be standard in almost any runtime.
e.g.
const m = new Map([["greeting", "hello"], ["salutation", "welcome"]])
See Also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
I have an array of approximately 19.000 items.
I'll have to access them by an arbitrary id at random (that is, there's no need to traverse the array)
I was just wondering if js con optimize the code if I use the id as the index of the array, or if there's any kind of trick or library to speed up these kind of things.
To be more precise, I'll have the results of an election in approximately 20k schools, and I'd like to know your advice about which one would be faster:
[
{
school_id: xx
results: [
{
party_id: xx
votes: xx
}, [...]
]
}, [...]
]
[ // use school_id as index to the array
[
{
party_id: xx
votes: xx
}, [...]
], [...]
]
The question is if js is smart enough to optimize array random access.
And any tool you could advice me to use to test the performance would be much welcome
These questions are always engine-dependent. In V8 (Google Chrome, Node.js):
Objects and Arrays are not radically different. For implementation simplicity, all objects have an external elements array where properties that are positive integers are stored.
So when you do obj[5], it doesn't matter if obj is the Javascript Array object or any javascript object - it will access the object's external elements array.
So if you created an object like this:
var a = {
a: 3,
b: 4,
c: {},
5: 5,
6: 6
};
The object layout will be:
[HiddenClassPointer, PropertiesArrayPointer, ElementsArrayPointer, TaggedSmallInteger(3), TaggedSmallInteger(4), JSObjectPointer]
Note how the named fields are stored side by side with the internal fields. If you now add any property after the fact, it will
go into the external properties array pointed by the second field instead of stored on the object directly.
The "fields" with the integer key would be in the external elements array pointed to by ElementsArrayPointer like this:
[HiddenClassPointer, TaggedSmallInteger(25), TheHolePointer, TheHolePointer, TheHolePointer, TheHolePointer, TheHolePointer, TaggedSmallInteger(5), TaggedSmallInteger(6), ...more hole pointers until 25 elements]
The 25 is length of the backing array. I will come back to that soon.
The hole pointer is needed to disambiguate between explicit undefined values given from the user and actual holes in the array. When you try to retrieve a[3], it will
return you undefined because there was a hole. So the actual hole object is not returned to user. So there are actually 3 different types of null :P
The initial length of 25 comes from the formula (initial index + 1 ) + ((initial_index + 1 ) / 2) + 16 so 6 + 7/2 + 16 = 25. You can see it in a heap snapshot.
( 108 - 8 ) / 4 === 25
Write a test using JSPerf. You can test a number of different scenarios using it.
Initializing a map having string keys can be performed as following in Javascript:
var xxx = {
"aaa" : ["a1","a2","a3"],
"bbb" : ["b1","b2","b3"],
"ccc" : ["c1","c2","c3"],
...
};
I need to initialize some map with integer keys, something like:
var xxx = {
0 : ["a1","a2","a3"],
1 : ["b1","b2","b3"],
2 : ["c1","c2","c3"],
...
};
Of course, I could proceed with an array like this:
xxx[0]=["a1","a2","a3"];
xxx[1]=["b1","b2","b3"];
xxx[2]=["c1","c2","c3"];
...
but the issue is that it makes the initialization code long. I need to squeeze all the bytes I can from it, because I need to push this object on the user side and any saved byte counts.
The xxx object needs to be initialized with n arrays, and each entry has a unique associated id between 0 and n-1. So, there is a one to one mapping between the id I can use and the arrays, and these ids are consecutive from 0 to n-1, which makes me think I could use an array instead of a Javascript 'map'.
I have noticed that one can push objects into an array. May be I could use something like this:
var xxx = [];
xxx.push(["a1","a2","a3"],["b1","b2","b3"],["c1","c2","c3"],...);
Is this a proper to achieve my objective or is there something smarter in Javascript? Thanks.
P.S.: Later, xxx will be referenced with something like xxx[2], xxx[10], etc...
This strikes me as cleaner than using String or integer keys, though, unless you need to add additional properties on xxx:
var xxx = [
["a1","a2","a3"],
["b1","b2","b3"],
["c1","c2","c3"]
//...
];
Just make xxx into an array containing arrays. You can get at, say, "b3", via xxx[1][2] (because xxx[1] == ["b1", "b2", "b3"].)
I need to map specific numbers to string values. These numbers are not necessarily consecutive, and so for example I may have something like this:
var obj = {};
obj[10] = "string1";
obj[126] = "string2";
obj[500] = "string3";
If I'm doing a search like this obj[126] would it be faster for me to use an object {} or an array []?
There will be no difference. ECMAScript arrays, if sparse (that is don't have consecutive indices set) are implemented as hash tables. In any case, you are guaranteed the O(n) access time, so this shouldn't concern you at all.
I created a microbenchmark for you - check out more comprehensive test by #Bergi. On my browser object literal is a little bit slower, but not significantly. Try it yourself.
A JS-array is a object, so it should not matter what you choose.
Created a jsperf test (http://jsperf.com/array-is-object) to demonstrate this.
Definetely an object should be the best choice.
If you have such code:
var arr = [];
arr[10] = 'my value';
, your array becomes an array of 11 values
alert(arr.length); // will show you 11
, where first 10 are undefined.
Obviously you don't need an array of length 1000 to store just
var arr = [];
arr[999] = 'the string';
Also I have to notice that in programming you have to chose an appropriate classes for particular cases.
Your task is to make a map of key: value pairs and object is the better choice here.
If your task was to make an ordered collection, then sure you need an array.
UPDATE:
Answering to your question in comments.
Imagine that you have two "collections" - an array and an object. Each of them has only one key/index equal to 999.
If you need to find a value, you need to iterate through your collection.
For array you'll have 999 iterations.
For object - only one iteration.
http://jsfiddle.net/f0t0n/PPnKL/
var arrayCollection = [],
objectCollection = {};
arrayCollection[999] = 1;
objectCollection[999] = 1;
var i = 0,
l = arrayCollection.length;
for(; i < l; i++) {
if(arrayCollection[i] == 1) {
alert('Count of iterations for array: ' + i); // displays 999
}
}
i = 0;
for(var prop in objectCollection) {
i++;
if(objectCollection[prop] == 1) {
alert('Count of iterations for object: ' + i); // displays 1
}
}
Benchmark
In total:
You have to design an application properly and take into account possible future tasks which will require some different manipulations with your collection.
If you'll need your collection to be ordered, you have to chose an array.
Otherwise an object could be a better choice since the speed of access to its property is roughly same as a speed of access to array's item but the search of value in object will be faster than in sparse array.
I have a variable length array of strings declared in javascript that contains Dungeons and Dragons class names. An example of this is below:
var class_names = new Array("Wizard", "Wizard", "Wizard", "Sorcerer",
"Sorcerer", "Ultimate Magus");
In my HTML, I use the javascript window.onload function to set a variety of variables from the javascript file to build the content of the page being displayed locally.
For things like name, this is easy:
document.getElementById('charname').innerHTML = name[0];
But for the class info, I don't want to just pump out a massive string of class names, I want it condensed down. Using the example 'class_names' above, I want to end up with a string that looks like this:
"Wizard 3, Sorcerer 2, Ultimate Magus 1"
i.e. the number after each class name should be the number of repetitions found in the array.
Anyone have an idea how to make this happen on the fly, so when I alter the javascript file to add more class data to class_names, it is displayed appropriately on my HTML page?
Thanks in advance for any help I get on this pet project (namely creating a HTML page for each character in my campaign that can be printed out as a character sheet....it's far better than manually writing a page for each character, or handwriting it on vanilla sheets).
It's easy enough, just loop through the array and count repetitions.
var l = class_names.length, i, tmp = {}, ret = [];
for( i=0; i<l; i++) {
if( !tmp[class_names[i]]) tmp[class_names[i]] = 0;
tmp[class_names[i]]++;
}
for( i in tmp) {
if( tmp.hasOwnProperty(i)) {
ret.push(i+" "+tmp[i]);
}
}
// output is ret.join(", ");
I think there are many ways to solve your problem...
Possibility A:
If you don't know if the classes are appearing in the right order, try to sort your Array first to ensure that they are grouped properly.
Iterate over the array and count the repetitions, i.e. increase your counter if
lastElement === class_names[i]
and append the result for the last class name to the result string and set the counter back to 1 otherwise.
Possibility B:
Store your Array directly as ["Wizard", 3, "Sorcerer", 2, ...] - this is possible since JS does not require arrays to contain the same type of element at each position.
Possibility C:
Use a different structure, e.g. using objects:
var class_names = [{name: "Wizard", level: 3}, {name: "Sorcerer", level: 2}, ...]