When to use Map over {} [duplicate] - javascript

This question already has answers here:
Map vs Object in JavaScript
(15 answers)
Maps vs Objects in ES6, When to use?
(6 answers)
Closed 1 year ago.
When to use Map over {}. Everything that could be done with Map could also be achieved using {} i.e. Object except one thing which is setting key of type other than string.
var mp = new Map();
mp.set(1, 'one');
mp.set(2, 'two');
var ob = {};
ob[1] = 'one';
ob[2] = 'two';
console.log(mp);
console.log(mp.get(1));
console.log(typeof mp.keys().next().value);
console.log(ob);
console.log(ob[1]);
console.log(typeof Object.keys(ob)[0]);
Output :
Map { 1 => 'one', 2 => 'two' }
one
number
{ '1': 'one', '2': 'two' }
one
string
Which method should be used at what scenarios when I can do the some thing with any of them.

mozilla documentation is enough descriptive, I believe.
The Map object is a simple key/value map. Any value (both objects and primitive values) may be used as either a key or a value.
Objects and maps compared
Objects are similar to Maps in that both let you set keys to values,
retrieve those values, delete keys, and detect whether something is
stored at a key. Because of this (and because there were no built-in
alternatives), Objects have been used as Maps historically; however,
there are important differences between Objects and Maps that make
using a Map better:
An Object has a prototype, so there are default keys in the map. This
could be bypassed by using map = Object.create(null) since ES5, but was seldom done.
The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and or any primitive.
You can get the size of a Map easily with the size property, while the size of an Object must be determined manually.
This does not mean you should use Maps everywhere, objects still are
used in most cases. Map instances are only useful for collections, and
you should consider adapting your code where you have previously used
objects for such. Objects shall be used as records, with fields and
methods. If you're still not sure which one to use, ask yourself the
following questions:
Are keys usually unknown until run time, do you need to look them up dynamically?
Do all values have the same type, and can be used interchangeably?
Do you need keys that aren't strings?
Are key-value pairs often added or removed?
Do you have an arbitrary (easily changing) amount of key-value pairs?
Is the collection iterated?
Those all are signs that you want a Map for a collection. If in
contrast you have a fixed amount of keys, operate on them
individually, and distinguish between their usage, then you want an
object.

Here is a bug I once had:
var scrabbled = {
The: 6,
word: 8,
length: 10,
is: 2,
weird: 9
};
$.each(scrabbled, function(k, v) {
console.log(k, v);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
(jQuery fixed it in 1.9, but it was not a fun day at work till I finally realised what was wrong.)
Yes, I suppose if you restrict yourself to string indices, there's nothing you can do with Map that you can't do with objects; but there's easy things to mess up with objects if you are not careful and doing things in a safe way (like iterating with for...in... without .hasOwnProperty check).

From the MDN,
An Object has a prototype, so there are default keys in the map. This
could be bypassed by using map = Object.create(null) since ES5, but
was seldom done.
The keys of an Object are Strings and Symbols,
whereas they can be any value for a Map, including functions, objects,
and or any primitive.
You can get the size of a Map easily with the
size property, while the size of an Object must be determined
manually.
Maps are usually useful for a collection where your keys are not string and key-value pairs are often removed or added. If your keys are fixed than an object will be better suited.

The difference is Object have strings as key, whereas Map can have any type of key.
If you have a number, and a number as string, then with objects, the key is the same (both get if they are not strings converted to strings), but with maps, the two keys are different and points to different values.
var m = new Map;
m.set(1, 'number 1');
m.set('1', 'string 1');
console.log(m.get(1)); // 'number 1'
console.log(m.get('1')); // 'string 1'
var o = Object.create(null); // an empty object without prototypes
o[1] = 'number 1';
o['1'] = 'string 1';
console.log(o[1]); // 'string 1'
The main reason to use an object over a map is, object works on every systems and maps only with on newr browsers, at least where ES6 is working.

Related

How to create a complex Javascript object

I am new to JavaScript and Python, and programming in general.
I want to store data about common English phonograms in a JavaScript data object. There are about 80 phonograms. Each phonogram has one or more possible pronunciations. Each phonogram’s pronunciation would have a list of one or more word examples (say, 30 maximum) which would include the IPA phonetic symbols and a translation to a foreign language. E.g., the phonogram ‘ea’ has three pronunciation,
(1) 'iːˈ, (2)ˈɛˈ & (3)ˈeɪˈ:
(1)ˈbeadˈ, 'feat', 'beat'...
(2)'bread', 'head', 'dead'...
(3)'break'...
Is there a built-in data structure best suited for this? I am thinking of a class to make these objects and store it in an array or something. And how should I write my text for populating the the data objects?
JavaScript has four fundamental structured data types:
objects, which have properties, which have keys (names which are strings or Symbols) and values (any type)
arrays, which have elements, which have indexes and values (arrays are technically objects, but ignore that for now)
Map, which has entries, which have keys (any type) and values (any type)
Set, which has unique entries of any type (probably not useful for what you're doing)
It sounds like you'd probably want either an object or a Map where the keys are the phonographs and the values are objects. Within each object, you'd probably have another Map or object keyed by the pronunciation where the values are objects giving further information (examples and translations).
Here's an example using Maps, which you initialize by passing an array of arrays into the Map constructor:
const data = new Map([
[
"ea",
{
pronunciations: new Map([
[
"iː",
{
examples: ["bead", "feat"],
transations: [/*...*/]
}
]
]),
otherInfo: /*...*/
}
],
// ...the other 79 entries...
]);
Getting the data for an entry based on the phonogram:
const entry = data.get("ea");
The entry object will have a pronunciations property with a Map of the pronunciations and the objects (with examples and translations) they map to.
More on MDN:
Map
Object
Array
map of an array should do the work.
map key can contain an identifier while the map value is an array that can contain the pronunciation.
map is a (key, value) pair where value will be an array/[] in your case.

What is happening under the hood in javascript when using the Array brackets notation

I have generally found javascript to be transparent, in that there are very few black boxes where "magic" just happens and you should just accept and look the other way, however I have not found any answer to how the Array brackets [] notation actually works under the hood.
let arr = [4, 5, 6, 7]
console.log(arr[3]) // <- How does this work?
What is javascript doing to access the item at index 3. Does it internally call some method on the Array.prototype?
With an object, the [] is a shortcut for a property accessor.
let obj = {
a: 'hello',
b: 'world'
}
obj['a'] === obj.a // true
Is an array then just an object with a long list of integer based properties?
let objArray = {
0: 'hello',
1: 'world'
}
let realArray = ['hello', 'world']
objArray[0] === 'hello' // true
realArray[0] === 'hello' // true
objArray.0 // SyntaxError: Unexpected number
realArray.0 // SyntaxError: Unexpected number
I have seen many many online discussions that all come to the conclusion that you cannot overload the brackets notation to truly subclass an Array but I have never seen an explanation on what magic is happening under the hood that allows the Array to work the way it does.
The obvious follow up question is whether there is any way to intercept the bracket notation access to define your own behavior, but I think I already know the answer to that.
You'd probably have to look at the implementation code to know precisely what's going on, but the basic idea is that arrays are actually layered atop objects.
This is backwards:
With an object, the [] is a shortcut for a property accessor.
The bracket notation is more fundamental. Thus, obj['foo'] and obj.foo work the same, but there is no equivalent for obj['foo & bar'], which is perfectly legitimate, and will respond with a value if obj has a key named "foo & bar".
Is an array then just an object with a long list of integer based properties?
Not quite, but you're not far off. Arrays are objects with the Array prototype, and with a little bit of additional magic to set the length property when new keys are added, or remove keys when that length is set.
And no, you cannot override the [] operator for your own purposes.
Is an array then just an object with a long list of integer based properties?
Yes, in it's simplest form, an Array is an Object with a list of integer base properties that is based on the Array prototype (which gives access to all the array methods like map, forEach, etc.)
As for intercepting the bracket notation, no, I have not seen anything that would allow that besides creating your own Object that has the methods you need (and then only access that object via the appropriate methods).
More info from MDN:
Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations. Neither the length of a JavaScript array nor the types of its elements are fixed. Since an array's length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are not guaranteed to be dense; this depends on how the programmer chooses to use them. In general, these are convenient characteristics; but if these features are not desirable for your particular use, you might consider using typed arrays.
Arrays cannot use strings as element indexes (as in an associative array) but must use integers. Setting or accessing via non-integers using bracket notation (or dot notation) will not set or retrieve an element from the array list itself, but will set or access a variable associated with that array's object property collection. The array's object properties and list of array elements are separate, and the array's traversal and mutation operations cannot be applied to these named properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#Array_object

Quickly creating a Map of objects from an Array of objects using a specified object key

I have an array of uniform objects:
var objects = [{
id: 1,
name: "one"
}, {
id: 2,
name: "two"
}];
And I'm converting these to a Map by using the id property of each object as Map keys:
var map = new Map();
objects.forEach(obj => map.set(obj.id, obj));
However, I'd like to do the conversion without:
having to manually create a new map object myself, and
having to manually iterate over the array and calling set myself
Additionally, I don't feel I should be making a utility function for this presumably native functionality.
Note: search queries for js array to map, or js convert array to map, or js map from array are all bloated with stone-age-era answers with Object, or solutions with a utility function. I'm looking for the native approach here.
I.e. I'm looking for the native JavaScript equivalent of mapping an array to a dictionary in C#, for example.
var map = list.ToDictionary(item => item.id);
This is so straightforward and convenient, yet interestingly enough, there is no Map.from in JavaScript (even though there is an Array.from).
I did my research while writing up the question, and I feel I should leave the solution here, as its possible practical applications are many.
I'm looking for the native JavaScript equivalent of mapping an array to a dictionary in C#
Considering a Map can be constructed with an iterable of 2-element arrays, where the first element of each inner array is used as the key, and the second element is used as a value, I believe this is the native JS equivalent, also the shortest:
new Map(objects.map(obj => [obj.id, obj]));
Live demo

Non-functionals of Arrays in JavaScript [duplicate]

The difference between a JavaScript Array, and Object is not very big. In fact it seems Array mainly adds the length field, so you can use both Arrays and Objects as numeric arrays:
var ar = new Array();
ar[0] = "foo";
ar["bar"] = "foo";
var ob = new Object();
ob[0] = "foo";
ob["bar"] = "foo";
assert(ar[0] == ob[0] == ar["0"] == ob["0"] == ar.bar == ob.bar); // Should be true.
So my questions is, in popular JavaScript engines (V8, JavaScriptCore, SpiderMonkey, etc.), how is this handled? Obviously we do not want our arrays to be actually stored as hash maps with key values! How can we be reasonably sure our data is stored as an actual array?
As far as I can see there are a few approaches engines could take:
Array is implemented exactly the same way as Object - as an associative array with string keys.
Array is a special case, with a std::vector-like array backing the numeric keys, and some density heuristic to prevent insane memory use if you do ar[100000000] = 0;
Array is the same as Object, and all objects get a heuristic to see if using an array would make more sense.
Something insanely complicated that I haven't thought of.
Really this would be simpler if there were a proper array type (cough WebGL typed arrays cough).
In SpiderMonkey, arrays are implemented basically as C arrays of jsvals. These are referred to as "dense arrays". However, if you start doing un-array-like things to them -- like treating them like objects -- their implementation is changed to something which very much resembles objects.
Moral of the story: when you want an array, use an array. When you want an object, use an object.
Oh, a jsval is a sort of variadic type which can represent any possible JavaScript value in a 64 bit C type.
In V8 and Carakan (and presumably Chakra), all (non-host) objects (both those that are arrays and those that aren't) with properties whose names are array indexes (as defined in ES5) are stored as either a dense array (a C array containing some value wrapper) or a sparse array (which is implemented as a binary search tree).
The unified object representation shows through in that it affects enumeration order: with an object, SpiderMonkey and SquirrelFish both give all properties in insertion order; and with an array, they in general (there are special cases in SM at least!) array indexes first then all other properties in insertion order. V8, Carakan, and Chakra always give array indexes first then all other properties in insertion order, regardless of object type.

Can an Object object be coerced into an Array object?

Crockford writes in http://javascript.crockford.com/survey.html:
"There are two ways to make a new array:
var myArray = [];
var myArray = new Array();"
So I'm confused by these two lines in some AJAX code I am reading:
var obj={}; // obj is an Object object (i.e. a hash table)
obj[4] = 'x'; // now obj is suddenly an Array object via an integer key?
In JavaScript are an object and an array really just the same thing, but with a variant on the key type?
In other words, is this the same as in php where we can use either a name (string) or an integer for a hash key?
I've Googled for an answer on this but can't seem to nail down an article which discusses this issue.
One possibility that comes to mind is that perhaps the first line is syntactic lint because the 2nd line overwrites the previous definition of obj as it creates a new Array object.
it does not become an array, it is simply an Object with a '4' property, like this:
var obj = {
'4': 'x'
};
it is just converted to a string when used as a property like obj['4'] = 'x';
Everything but primitive datatypes is an object in JavaScript. Objects can have a properties and there are two ways to access object properties:
Dot notation, foo.bar, which you can use as long as the property name is a valid identifier.
Bracket notation, foo['bar'] which you have to use if the key is not a valid identifier [spec]. For example, if it is a number, or contains a space or you have a variable with the name.
Hence, bracket notation is not a characteristic of arrays and if you see it, it does not mean the value is an array. It is simple one of two ways of accessing properties.
The elements of an array are just properties with numeric keys. Arrays are built on top of objects and implement some additional methods which treat these numeric properties in a special way. For example the .length property is automatically updated when you add new elements. But ultimately they are just normal properties.
In your example you have a simple object. You have to access the property with obj[4] or obj['4'] because obj.4 is invalid since 4 is not a valid identifier (basically everything that you can use as variable name is a valid identifier. var 4 = 'foo'; is invalid).
And since arrays are just objects, if you could use numbers as identifiers, you were also able to access an element with arr.4.
As far as I know, no, an object can't be coerced into an array. But, it can look and act like an array, and that's what's happening here. Numbers, and anything else that can be coerced to a string, are perfectly valid property names for Javascript objects, so
obj[4] = 1;
obj['spam'] = 2;
are both valid ways of setting a property on the object. That doesn't make the object an array. An Array is a special class of object with specific methods (.slice(), .concat(), etc) and a length property that's kept up to date with the number of items in the array.
Yes
Javascript Array is very different from tradition array, you can think of it as object.
var array = [1,2,3] is equivalent to var object = {'0' : 1, '1' : 2, '2' : 3}
except array inherited from Array.prototype and object inherited from Object.prototype, where Array.prototype will contain method such as length.
Javascript is a loosely-typed, prototype-based language. Even primitive types like a boolean can be treated like an object (though you aren't going to get far). Almost everything in javascript is, at root, an object.
Understanding this, an array IS an object. You can arbitrarily add properties to any object:
var xml = new XMLHttpRequest();
xml[4] = 'x';
console.log(xml);
That object is still an instance of XMLHttpRequest. It now has a property labeled 4 with a value of x. You can treat anything like this -- even a function:
var test_func = function () {
alert('woah!');
}
test_func[4] = 'x';
console.log(test_func[4]);
The take-away here is that the obj[key] = value notation is NOT indicative of an "array" type, like it is in languages such as PHP. Rather, it is an alternate way to access properties of any object, and is equivalent to obj.key = value (you can't use obj.4 = 'x', though, that's invalid syntax). The other take-away is that any object in javascript can be modified or used in pretty much any way. You shouldn't misuse objects, but you can
Check it out here: http://jsfiddle.net/w2AqJ/
Documentation
Array on MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
Javascript "associative arrays" considered harmful by Andrew Dupont - http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/

Categories