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?
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.
If you are desperate you can do like this
var str = 'abcdeaadbc';
var chars = new Set(str);
document.write("<pre>" + JSON.stringify([...chars].map(c => c+"up")) + "</pre>")
Because even if map
is generic enough to work for arbitrary array-like objects, not all iterable objects are array-like.
map
just gets the length
property, and then iterates properties from 0
to length-1
. That can't work with sets because they have a size
instead of length
, and they don't provide random access to its elements.
If you really want to use map
, you will need to transform the set to an array, and then transform the result back to a set.
var newSet = new Set(Array.from(set).map(myFunc));
But there is no point in doing that, it would be better to use set operations only:
var newSet = new Set();
for(let val of set) newSet.add(myFunc(val));
Or maybe a self-invoked generator function:
var newSet = new Set(function*() {
for(let val of set) yield myFunc(val);
}());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With