I am trying to learn about ES6 Map data structures and am having difficulty understanding some of their behaviour. I would like to create a Map with an Array as a value and append (push) new values onto the current value of the Map. For example:
let m = new Map()
m.set(1, []) // []
m.set(1, m.get(1).push(2)) // [[1, 1]]
I am confused as to why I do not get [2] as the value of m.get(1) above. How can I append values to the array in my map?
That's because the method push returns the size of the array after the insertion.
You can change your code to the following to append to an array:
m.get(1).push(2);
And it'll update the value in the map, there's no need to try to re-set the value again as the value is passed back as reference.
The best way to define a Map according to your need is to explicitly tell the Map, what kind of data you want to deal with.
in your case you want values in an array, we could get using a "string id" for example
In this case you will have this :
let map = new Map<String, Array<any>>
Then you can create items like map["key"] = ["lol", 1, null]
There is two thing. First as #Adriani6 said the push method do not returns a pointer to the array but the size of the array.
Secondly, you do not need to do an other m.set, because your push will affect directly the array behind the reference returned by m.get
function displayMap(m) {
m.forEach(function(val, key) {
console.log(key + " => " + val);
});
}
let m = new Map();
m.set(1, []);
displayMap(m);
m.get(1).push(20);
displayMap(m);
It fails, because the return of push() is the size of the array after push.
You can push the content after doing a get().
m.get(1).push(2);
If you want to test set() then write a a self executable function like this:
let m = new Map()
m.set(1, []) // []
console.log(m.get(1))
m.set(1, (() => {m.get(1).push(2);return m.get(1);})());
console.log(m.get(1))
Here's a working example of what you are trying to do (open console)
Have a look here. As you can see the push method returns the new length of the array you just mutated, hence your result.
Related
Is there a way to use Set as object keys
let x = {}
const a = new Set([3, 5])
x[a] = 1
console.log(x) // >{[object Set]: 1}
const b = new Set([1, 4])
x[b] = 2
console.log(x) // >{[object Set]: 2}
The keys are being overwritten even though the sets are not equal.
Thanks!
No this is not possible because Object keys must be strings or symbols. If you would like to use a Set as a key you can try using a Map. Maps are similar to objects except you can use other objects as keys for a map.
One thing to keep in mind is that you cannot use maps exactly like you use Objects.
This is directly from the Mozilla docs.
The following IS NOT A VALID USE OF A MAP.
let wrongMap = new Map()
wrongMap['bla'] = 'blaa'
wrongMap['bla2'] = 'blaaa2'
console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
But that way of setting a property does not interact with the Map data structure. It uses the feature of the generic object. The value of 'bla' is not stored in the Map for queries. Other operations on the data fail:
Correct use of a map looks like the below:
let map = new Map()
// setting values
map.set(key, value)
// getting values
map.get(key)
Remember that if you use an Object like a Set as a key, the reference of the Set is what matters.
If you instantiate two sets separately, even if they both have the same contents, they will have different references and be considered different keys.
Do you mean that the map in ES6 like this:
x = new Map()
a = new Set([3, 5])
x.set(a, 1)
console.log(x);
Why chaining function on array.flat not working as expected
const input = [[[{"type":"banana"},{"type":"orange"}]]];
console.log(input.flat(2).push({"type":"grapes"}));
What I expect is, it should remove the 2 array wrapper and push a new item to input
But I get only the length why is that ?
Array#push returns the new length of the array.
You could concat the array (Array#concat) with a new element and get the new array.
console.log(input.flat(2).concat({ type: "grapes" }));
Array.push mutates the array itself and returns only the length of the array. You can log the array in the next line after the push statement.
The push method returns the new length of the array and mutates it.
Try instead:
const input = [[[{"type":"banana"},{"type":"orange"}]]];
const flattened = input.flat(2)
flattened.push({"type":"grapes"});
console.log(flattened)
/*
[
{type:"banana"},
{type:"orange"},
{type:"grapes"}
]
*/
You can also other than Array.concat also spread into a new array to achieve the same result:
const input = [[[{"type":"banana"},{"type":"orange"}]]];
console.log([...input.flat(2), {"type":"grapes"}]);
The issue in your code however is that Array.flat returns a whole new array for you to work with and Array.push simply returns the new length of the array (so you need to keep a reference to the array). So with some refactoring:
const input = [[[{"type":"banana"},{"type":"orange"}]]];
let newArr = input.flat(2) // <-- this would return a new array
newArr.push({"type":"grapes"}) // We do not care what push returns since we have a reference to the array
console.log(newArr)
You get the expected result
Also note of caution with Array.flatMap and Array.flat ... both do not have support in IE and you would need a polyfill
So...there is this type in js called Map and it is really nice...it is faster than an Object for iterations and calculations so I really like it. However...you can't pass Maps around as you could with objects.
I know that I could turn Map into JSON but it is costly to do so and it kind of looses the point of using Maps in the first place.
JSON format is not meant for Maps...it was meant for Objects.
So...lets move from the JSON format a little.
Is there a way for me to serialize a Map into a string in any way so that I can then do the opposite - from said serialized Map to end up with a Map
Preferably this method should be as easy to perform as JSON.stringify or its counterpart JSON.parse.
I want to use Map as it is faster and better but I need to send my data as string. The format of the string is not important as long as I can parse it back into a Map
-- edit: Added the missing JSON Stringify function during serialization -
There is a native way of doing this in Javascript.
the Map object has a method called entries() that returns an iterator that will yield each entry in the map as an array with 2 values in it. It will look like [key, value].
To avoid writing any looping code yourself, you can use Array.from() which can consume an iterator and extract each item from it. Having that, the following will give you your Map object in a serialized form.
let mySerialMap = JSON.stringify(Array.from(myMap.entries()))
console.log(mySerialMap)
Now, that's not even the coolest part. But before getting there, let me show you one possible way of using the Map constructor.
let original = new Map([
['key1', 'the-one-value']
['second-key, 'value-two']
])
The array of array that you can see being passed to the Map object is in the same format as what you get from using Array.from(myMap.entries()).
So you can rebuild you map in a single line using the following sample code:
let myMap = new Map(JSON.parse(mySerialMap))
And you can use myMap as you would any Map.
let myMap = new Map(JSON.parse(mySerialMap))
let first = myMap.get('some-key')
myMap.set('another-key', 'some value')
I guess the whole point of Maps/Dictionaries is that you can use objects as keys in them, so:
let a = {}, b = {}, m = new Map();
m.set(a,b);
m.get(a); // b
So you get b since you have a reference on a. Let's say you serialize the Map by creating an Array of arrays, and stringify that to json:
function serialize (map) {
return JSON.stringify([...map.entries()])
}
let s = serialize(m); // '[[{}, {}]]'
// '[[<key>, <val>], … ]'
Than you could:
let m2 = JSON.parse(s).reduce((m, [key, val])=> m.set(key, val) , new Map());
But the question now is: How to get a certain key? Since there does not exist any reference, due to the fact that all objects have been stringified and recreated, it is not possible to query a dedicated key.
So all that would only work for String keys, but that really takes most of power of maps, or in other words, reduces them to simple objects, what is the reason maps were implemented.
To #philipp's point, people who care about the serialization of the Map will probably prefer objects (leverages intuition, reduces '[]' arithmetic). Object.entries() and Object.fromEntries() can make that a bit more literate:
writeMe = new Map()
writeMe.set('a', [1])
writeMe.set('b', {myObjValue: 2})
// ▶ Map(2) {'a' => Array(1), 'b' => {…}}
written = JSON.stringify(Object.fromEntries(writeMe.entries()))
// '{"a":[1],"b":{"myObjValue":2}}'
read = new Map(Object.entries(JSON.parse(written)))
// ▶ Map(2) {'a' => Array(1), 'b' => {…}}
read.get("b")
// ▶ {myObjValue: 2}
I am trying to reverse an array which is an element in an object.
colorKey = {
"2m":["#ffffff","#000000"]
}
colorKey["2mi"] = colorKey["2m"];
Array.reverse(colorKey["2mi"])
This is not working and returning colorKey["2mi"] the same as colorKey["2m"]. When I run the same command in developer console in browser, it reverses successfully. Where is the problem?
This is no static method off Array called reverse. reverse is an instance method (Array.prototype.reverse) off the Array object, so the instance of the Array must be the caller.
This solves your problem:
colorKey = {
"2m":["#ffffff","#000000"]
}
colorKey["2mi"] = colorKey["2m"];
colorKey["2mi"].reverse();
Output:
["#000000", "#ffffff"]
Calling reverse() for an array mutates it (reverse is in place - a new array is not created). What you want, apparently, is to have a reversed copy of the array. Basically, create a new array with the same values and then reverse it.
var a = [1, 2], b;
b = a.slice(0).reverse();
Slice creates a new array with the same elements (but remember that it is not cloning the elements).
#Rajat Aggarwal
What you are asking for, is to clone your previous array in reverse order.
The only trivial part of it would be reversing it. Because there is no way of cloning Objects and Arrays, nor a general method that you could write down as a function to be using it universally.
This specific array from the sample you provided can be cloned easily because it is flat and it only contains primitives. But the solution to it, will be exactly as specific as the sample array provided.
A specific solution to this task would be to use a plain coma-separated string of successive values and convert that to specific arrays of their corresponding primitive values.:
var colors = "#ffffff,#000000";
var colorKey = {
"2m":colors.split(","),
"2mi":colors.split(",").reverse()
}
which will yield you a:
>> colorKey
{
2m : #ffffff,#000000,
2mi : #000000,#ffffff
}
Lets say I have the following map:
let myMap = new Map().set('a', 1).set('b', 2);
And I want to obtain ['a', 'b'] based on the above. My current solution seems so long and horrible.
let myMap = new Map().set('a', 1).set('b', 2);
let keys = [];
for (let key of myMap)
keys.push(key);
console.log(keys);
There must be a better way, no?
Map.keys() returns a MapIterator object which can be converted to Array using Array.from:
let keys = Array.from( myMap.keys() );
// ["a", "b"]
EDIT: you can also convert iterable object to array using spread syntax
let keys =[ ...myMap.keys() ];
// ["a", "b"]
You can use the spread operator to convert Map.keys() iterator in an Array.
let myMap = new Map().set('a', 1).set('b', 2).set(983, true)
let keys = [...myMap.keys()]
console.log(keys)
OK, let's go a bit more comprehensive and start with what's Map for those who don't know this feature in JavaScript... MDN says:
The Map object holds key-value pairs and remembers the original
insertion order of the keys.
Any value (both objects and primitive
values) may be used as either a key or a value.
As you mentioned, you can easily create an instance of Map using new keyword...
In your case:
let myMap = new Map().set('a', 1).set('b', 2);
So let's see...
The way you mentioned is an OK way to do it, but yes, there are more concise ways to do that...
Map has many methods which you can use, like set() which you already used to assign the key values...
One of them is keys() which returns all the keys...
In your case, it will return:
MapIterator {"a", "b"}
and you easily convert them to an Array using ES6 ways, like spread operator...
const b = [...myMap.keys()];
I need something similiar with angular reactive form:
let myMap = new Map().set(0, {status: 'VALID'}).set(1, {status: 'INVALID'});
let mapToArray = Array.from(myMap.values());
let isValid = mapToArray.every(x => x.status === 'VALID');
Not exactly best answer to question but this trick new Array(...someMap) saved me couple of times when I need both key and value to generate needed array. For example when there is need to create react components from Map object based on both key and value values.
let map = new Map();
map.set("1", 1);
map.set("2", 2);
console.log(new Array(...map).map(pairs => pairs[0])); -> ["1", "2"]
Side note, if you are using a JavaScript object instead of a map, you can use Object.keys(object) which will return an array of the keys. Docs: link
Note that a JS object is different from a map and can't necessarily be used interchangeably!