4

I am using ES6 Set instances and I need to apply some transformations on them. These are transformations of the kind that would be simple if they were arrays. Here is an example:

let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = s.filter(val => val > 1); // TypeError, filter not defined
let n = Array.prototype.filter.call(s, val => val > 1); // []

I was hoping that the result would either be a new Set or an array. I similarly want to use other array comprehension methods like filter, map, reduce, etc. And I would also like to have similar behaviour on ES6 Map instances as well.

Is this possible, or do I need to be using vanilla JS arrays?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148

2 Answers2

6

you can get the values of s in an array using

Array.from(s.values())

Array.from documentation states that it creates a new Array instance from an array-like or iterable object.

Set.values returns a new Iterator object that contains the values for each element in the Set object in insertion order.

So your code becomes

let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = Array.from(s.values()).filter(val => val > 1)
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
1

You can't use Array methods directly on a Set or Map object. Array methods expect .length and [n] indexing which is not how Set or Map work.

You can either convert your Set to an array using Array.from(s) or you can create your own methods to operate directly on the Set or Map. If you're going to be doing this a lot and the desired end result is a Set or Map, then it's probably better to not convert to an Array, modify, then convert back. Plus, converting a Map to an array is not quite so simple since you have both a key and value (might have to be an array of objects).

For example, you could create your own .filter() method for a Set object like this:

Set.prototype.filter = function(fn) {
    let result = new Set();
    for (let val of this) {
       if (fn(val, this) === true) {
           result.add(val);
       }
    }
    return result;
}

let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = s.filter(val => val > 1); 


// log the output

// log output
document.write("Set {" + Array.from(n).join(", ") +"}");

Similar methods could be created for other Array methods.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I may end up doing something like this, but I prefer to go simpler at first. I awarded the answer to @Jaromanda X since he was first. – Andrew Eisenberg Feb 13 '16 at 00:44
  • @AndrewEisenberg - But, if you want to end up with a `Set`, there's really no reason to convert to an Array and then back to a Set just to keep from writing a couple more lines of code one time in a utility method - in my opinion. – jfriend00 Feb 13 '16 at 00:51
  • At this point there's no need to end up with a set since it is being used as part of an angularjs filter. In fact in the end, there will always need to be an array since order is important in the ui. – Andrew Eisenberg Feb 13 '16 at 01:00
  • @AndrewEisenberg - OK. If order is important, then I'm wondering why it was in a `Set` in the first place. – jfriend00 Feb 13 '16 at 01:02
  • The data is actually stored in a map, keyed by an index that the UI doesn't care about. This is to help make server updates happen quickly. The UI will display the data after putting it through a number of filters depending on the situation (eg- reducing, mapping, sorting, filtering). – Andrew Eisenberg Feb 13 '16 at 01:32