Yuriy Yakym Yuriy Yakym - 6 months ago 21
Javascript Question

Why can't I invoke Array.prototype.map with Set object

I want to get get unique characters from some string using ES6's Set.
So let's assume that we have string

var str = 'abcdeaabc';
and create set of characters from this string:

var str = 'abcdeaadbc';
var chars = new Set(str);


Now we have the set of unique characters:
abcd
.
I was surprised that
char
set has
forEach
method and has no
map
method.

Set is iterable object, so why can't we use
map
function to iterate over set?

I tried to pass
chars
set and
chars.values()
SetIterator to
Array.prototype.map
this way:

Array.prototype.map.call(chars, function(element) {...});
Array.prototype.map.call(chars.values(), function(element) {...});


But every try failed.

For example, let's assume that we want to get unique symbols from string and return an array of them with preceding underscore. eg.:
['_a', '_b', '_c', '_d']


Here are my two solutions:

// First:
var result = Array.from(chars).map(function(c) {
return '_' + c;
});

// Second:
var result = [];
chars.forEach(function(c) {
this.push('_' + c);
}, result);


But is there a way of invoking Array.prototype's
map
function with
Set
context? And if no - why?

Answer

Array.prototype.map works on array-like types that have integer indexes and a length property. Set and Map are general iterable types, but they are not array-like, so Array.prototype methods will not work on them. e.g.

var s = new Set([1, 2, 3]);
s.length === undefined;
s[0] === undefined

The main approach is Array.from kind of like your solution, but one thing to note is that Array.from takes a mapFn as the second argument, so you can do

let results = Array.from(chars, c => `_${c}`);

for a nice and short map to convert an iterable to an array.