What is difference between Map and Set? - javascript

JavaScript Map and Set objects are both iterable objects. Both store object by [key, value] pair.
When to use which? What is the difference between them?

Provided you are talking about the ES6 types, they aren't the same data structure even though the Set might be implemented with a Map.
Your definition of Map is right, but a Set is a collection of unique values, unlike an array which can have duplicates.
var array = [1, 2, 3, 3];
var set = new Set(array); // Will have [1, 2, 3]
assert(set.size, 3);
var map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
map.set('C', 3);
map.set('a', 4); // Has: a, 4; b, 2; c: 3, C: 3
assert(map.size, 4);

Summary:
Use a Set when your dataset needs to be composed of unique values
Use a Map when you have pairs of associated data. You map the keys to the values
Example Set:
There is a meeting with people coming from different organizations. Some people come from the same organization. We need to compose a list all the different organzations. For this we can use a set since we only want to include every organization once:
const organization = new Set();
organization.add('org1');
organization.add('org2');
organization.add('org3');
organization.add('org1');
organization.add('org3');
organization.add('org1');
for(let org of organization){
console.log(org);
}
Example Map:
We have a pack of dogs and want to assign an age to each dog. We want to map the unique name of each dog to the age of the dog:
const dogs = new Map([['fluffy', 10], ['barkie', 13]]);
dogs.forEach((value, key) => console.log(key, value));
How is Map different from an Object?
An Object is also a collection of key value pairs and can fulfill often the same purpose as a Map can (which is creating key-value pairs). However, there are some key differences between a Map and an Object:
Map is built in Iterable, this allows it to use the for of loop or its implementation of the forEach() method which an plain JS Object cannot use.
Map has some nice built in methods on its prototype which makes working with it very nicely. Because al Objects inherit from Object.prototype is has access to more useful methods. For example, the size() method on Map returns the number of keys in the Map.

var obj = {};
obj.name= "Anand Deep Singh";
console.log(obj.name); //logs "Anand Deep Singh"
similarly in ES6, we can use regular object.
var map = new Map();
map.set("name","Anand Deep Singh");
console.log(map.get("name")); //logs "Anand Deep Singh"
But noticeable thing is a Map isn’t created with the literal object syntax, and that one uses set and get methods to store and access data.
It has a has method to check whether the key exists in the object or not, delete method to delete the object and clear method to clear the entire object.
Set is a unique list of values. It’s simply a unique list.
var set = new Set(["a", "a","e", "b", "c", "b", "b", "b", "d"]);
console.log(set); //logs Set {"a", "e", "b", "c", "d"}
A Set can’t be accessed like an array, and it provides the same methods as a Map.

There are two main data structures:
Objects:
are used for storing keyed collections.
Arrays:
are used for storing ordered collections.
But that’s not enough for real life. That’s why Map and Set also exist.
Map:
is a collection of keyed data items, just like an Object. But the main difference is that Map allows keys of any type.
For instance:
let map = new Map();
map.set('1', 'str1'); // a string key
map.set(1, 'num1'); // a numeric key
map.set(true, 'bool1'); // a boolean key
let hamid = { name: "Hamid" };
// hamid is the key for the map
map.set(hamid, 123); // an object key
Set
: is a special type collection – “set of values” (without keys), where each value may occur only once.
instance:
let set = new Set();
let hamid= { name: "Hamid" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// visits, some users come multiple times
set.add(hamid);
set.add(pete);
set.add(mary);
set.add(hamid);
set.add(mary);
// set keeps only unique values
alert( set.size ); // 3
https://javascript.info/map-set

In Map(), keys can be of any type [String, number, object] except for
regular objects that must be strings.
The Set is a one-dimensional array with unique keys, while the Map is
a two-dimensional array with key-value pairs, where each key shall be
unique.
For Map(), we allow the use of other primitive types (including NaN). As a result, developers are able to link to other types of data.
A Set consists of a collection of unique values, while a Map is a pair of associated data when we map the keys to the values. Both Map and Set have similar methods, such as .has(), .get(), .delete(), and .size().
The difference is that a map has a key-value pair and two dimensions. It is possible to convert both 2D arrays and arrays to sets.
To summarise, Map is used for key-value pair collections, while Set is used for unique value collections.

if you have unique values that you want to put in the set, then you should use Set as it is made for unique values
else if you don't have any issues with 2 or more same values then you should prefer Map

Related

Check if array of objects includes an object

I am trying to check if an array of objects includes a object. I want it to return true when there is a object in the array that has the same values and the object id should not matter. This is how i thought it would work:
let arr = [{r:0, g:1}];
let obj = {r:0, g:1}
console.log(arr.includes(obj));
But it returns false and I need it to return true. Do I have to convert every object in the array to a string with JSON.stringify() and the object I am searching for like this:
let arr = [JSON.stringify({r: 0, g: 1})]
let obj = {r: 0, g: 1}
console.log(arr.includes(JSON.stringify(obj)));
Is there another easier and more efficient way to do it with more objects?
You get false because objects are compared by a reference to the object, while you got there 2 separate object instances.
Wile JSON.stringify might work, keep in mind that the order of properties is not guaranteed and it may fail if the order is not the same, because you get a different string.
you can check for an id property or compare several properties to match against, if you must you can compare all properties with a loop.
If you have an access to the object's reference, you can use a Map or a Set which allows you to store and check references
const obj = {r:0, g:1};
const obj2 = {r:0, g:1};
const mySet = new Set();
// given the fact that you do have access to the object ref
mySet.add(obj);
const isObjInList = mySet.has(obj);
const isObj2InList = mySet.has(obj2);
console.log('is obj in list - ', isObjInList);
console.log('is obj2 in list - ', isObj2InList);
JSON.stringify doesn't work as expected if you change the order of properties in one of the objects.
You can use .some in combination with isEqual from lodash (or other alternatives). Or you can write it by yourself, but be careful, there are too many edge cases, that's why I recommend using an existing approach. There is no need to reinvent the wheel.
let arr = [JSON.stringify({r: 0, g: 1})]
let obj = {g: 1, r: 0}
console.log(arr.includes(JSON.stringify(obj)));
let arr2 = [{r:0, g:1}];
let obj2 = {g:1, r:0};
console.log(arr2.some(item => _.isEqual(item, obj2)));
console.log(_.some(arr2, item => _.isEqual(item, obj2))); // more canonical way
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
I like to use Set() for this purposes, read from the documentation:
The Set object lets you store unique values of any type, whether primitive values or object references.
See the below example:
let obj = {r:0, g:1};
const set = new Set();
set.add(obj);
console.log(set.has(obj));
I hope that helps!
You can use the JavaScript some() method to find out if a JavaScript array contains an object.
This method tests whether at least one element in the array passes the test implemented by the provided function. Here's an example that demonstrates how it works:
// An array of objects
var persons = [{name: "Harry"}, {name: "Alice"}, {name: "Peter"}];
// Find if the array contains an object by comparing the property value
if(persons.some(person => person.name === "Peter")){
alert("Object found inside the array.");
} else{
alert("Object not found.");
}
Note that if try to find the object inside an array using the indexOf() method like persons.indexOf({name: "Harry"}) it will not work (always return -1). Because, two distinct objects are not equal even if they look the same (i.e. have the same properties and values). Likewise, two distinct arrays are not equal even if they have the same values in the same order.
The some() method is supported in all major browsers, such as Chrome, Firefox, IE (9 and above), etc. See the tutorial on JavaScript ES6 Features to learn more about arrow function notation.

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.

After trying this example, feeling confused on the basics of Maps and Set in Javascript

I am trying to apply some examples to better understand the difference of Map and Set and the behaviour of each one seems confusing. Look at the examples and output below
Map example:
let nameMap = new Map([
['name', 'stack'],
['name', 'overflow'],
['domain', 'technology']
]);
// iterate over keys (nameMap)
for (let name of nameMap) {
console.log(JSON.stringify(name));
}
output:
["name","overflow"]
["domain","technology"]
Set Example:
let nameSet = new Set([
['name', 'stack'],
['name', 'overflow'],
['domain', 'technology']
]);
// iterate over keys (nameSet)
for (let name of nameSet) {
console.log(JSON.stringify(name));
}
output:
["name","stack"]
["name","overflow"]
["domain","technology"]
My question is why map returns only the second occurence of two similar objects?
Set returns all three objects even though first two keys and values being same, while it supposed to delete one of them.
My question is why map returns only the second occurence of two similar objects?
Because a Map contains a set of key-value pairs. You can only have one value per key. In the constructor, each array is represents [key, value]). The second bit of data with the key 'name' overwrites the first one.
Set returns all three objects even though first two keys and values being same, while it supposed to delete one of them.
A set stores a set of unique values. Each array (like ['name', 'stack']) is one value.
Because Sets are like arrays - they just store the values in a list. Therefore, the constructor will add all three arrays to the new Set as values. The Map constructor is similar to Object.fromEntries - the first item in each sub-array is the key, the second the value. You can't store keys in a Set, only values - so the two items have to be exactly the same, and a primitive, to be excluded from the Set.
A Map is a container for key-value pairs. In your input, name and domain will become keys. Since a key can only exist once, the key name gets deduplicated.
A Set is an array of values which deduplicates the values themselves. Since there aren't any identical values in your input, all are retained.

When to use Map over {} [duplicate]

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.

What's the difference between ES6 Map and WeakMap?

Looking this and this MDN pages it seems like the only difference between Maps and WeakMaps is a missing "size" property for WeakMaps. But is this true? What's the difference between them?
They both behave differently when a object referenced by their keys/values gets deleted. Lets take the below example code:
var map = new Map();
var weakmap = new WeakMap();
(function(){
var a = {x: 12};
var b = {y: 12};
map.set(a, 1);
weakmap.set(b, 2);
})()
The above IIFE is executed there is no way we can reference {x: 12} and {y: 12} anymore. Garbage collector goes ahead and deletes the key b pointer from “WeakMap” and also removes {y: 12} from memory. But in case of “Map”, the garbage collector doesn’t remove a pointer from “Map” and also doesn’t remove {x: 12} from memory.
Summary: WeakMap allows garbage collector to do its task but not Map.
References: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/
Maybe the next explanation will be more clear for someone.
var k1 = {a: 1};
var k2 = {b: 2};
var map = new Map();
var wm = new WeakMap();
map.set(k1, 'k1');
wm.set(k2, 'k2');
k1 = null;
map.forEach(function (val, key) {
console.log(key, val); // k1 {a: 1}
});
k2 = null;
wm.get(k2); // undefined
As you see, after removing k1 key from the memory we can still access it inside the map. At the same time removing k2 key of WeakMap removes it from wm as well by reference.
That's why WeakMap hasn't enumerable methods like forEach, because there is no such thing as list of WeakMap keys, they are just references to another objects.
From the very same page, section "Why Weak Map?":
The experienced JavaScript programmer will notice that this API could
be implemented in JavaScript with two arrays (one for keys, one for
values) shared by the 4 API methods. Such an implementation would have
two main inconveniences. The first one is an O(n) search (n being the
number of keys in the map). The second one is a memory leak issue.
With manually written maps, the array of keys would keep references to
key objects, preventing them from being garbage collected. In native
WeakMaps, references to key objects are held "weakly", which means
that they do not prevent garbage collection in case there would be no
other reference to the object.
Because of references being weak, WeakMap keys are not enumerable
(i.e. there is no method giving you a list of the keys). If they were,
the list would depend on the state of garbage collection, introducing
non-determinism.
[And that's why they have no size property as well]
If you want to have a list of keys, you should
maintain it yourself. There is also an ECMAScript
proposal
aiming at introducing simple sets and maps which would not use weak
references and would be enumerable.
‐ which would be the "normal" Maps. Not mentioned at MDN, but in the harmony proposal, those also have items, keys and values generator methods and implement the Iterator interface.
Another difference (source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap):
Keys of WeakMaps are of the type Object only. Primitive data types as
keys are not allowed (e.g. a Symbol can't be a WeakMap key).
Nor can a string, number, or boolean be used as a WeakMap key. A Map can use primitive values for keys.
w = new WeakMap;
w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key
m = new Map
m.set('a', 'b'); // Works
From Javascript.info
Map -- If we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected.
let john = { name: "John" };
let array = [ john ];
john = null; // overwrite the reference
// john is stored inside the array, so it won't be garbage-collected
// we can get it as array[0]
Similar to that, if we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overwrite the reference
// john is stored inside the map,
// we can get it by using map.keys()
WeakMap -- Now, if we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference
// john is removed from memory!
WeakMap keys must be objects, not primitive values.
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); // works fine (object key)
// can't use a string as the key
weakMap.set("test", "Not ok"); // Error, because "test" is not an object
Why????
Let's see below example.
let user = { name: "User" };
let map = new Map();
map.set(user, "...");
user = null; // overwrite the reference
// 'user' is stored inside the map,
// We can get it by using map.keys()
If we use an object as the key in a regular Map, then while the
Map exists, that object exists as well. It occupies memory and may
not be garbage collected.
WeakMap is fundamentally different in this aspect. It doesn’t
prevent garbage-collection of key objects.
let user = { name: "User" };
let weakMap = new WeakMap();
weakMap.set(user, "...");
user = null; // overwrite the reference
// 'user' is removed from memory!
if we use an object as the key in it, and there are no other
references to that object – it will be removed from memory (and from
the map) automatically.
WeakMap does not support iteration and methods keys(), values(), entries(), so there’s no way to get all keys or values from it.
WeakMap has only the following methods:
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
That is obvious as if an object has lost all other references (like 'user' in the code above), then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens.
The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of a WeakMap is not known. The engine may have cleaned it up or not or did it partially. For that reason, methods that access all keys/values are not supported.
Note:- The main area of application for WeakMap is an additional data storage. Like caching an object until that object gets garbage collected.
WeapMap in javascript does not hold any keys or values, it just manipulates key value using a unique id and define a property to the key object.
because it define property to key object by method Object.definePropert(), key must not be primitive type.
and also because WeapMap does not contain actually key value pairs, we cannot get length property of weakmap.
and also manipulated value is assigned back to the key object, garbage collector easily can collect key if it in no use.
Sample code for implementation.
if(typeof WeapMap != undefined){
return;
}
(function(){
var WeapMap = function(){
this.__id = '__weakmap__';
}
weakmap.set = function(key,value){
var pVal = key[this.__id];
if(pVal && pVal[0] == key){
pVal[1]=value;
}else{
Object.defineProperty(key, this.__id, {value:[key,value]});
return this;
}
}
window.WeakMap = WeakMap;
})();
reference of implementation

Categories