129

new Set(['b', 'a', 'c']).sort() throws TypeError: set.sort is not a function. How can I sort a Set to ensure a particular iteration order?

ericsoco
  • 24,913
  • 29
  • 97
  • 127
  • 11
    Sets are unordered. – SLaks Oct 12 '15 at 20:34
  • 5
    @SLaks perhaps they are, but I'd like to be able to take advantage of the benefit of unique keys that `Set`s offer, while still being able to sort the elements. Java offers [`SortedSet`s](http://docs.oracle.com/javase/7/docs/api/java/util/SortedSet.html), I can only assume they did so because someone offered a valid use case... ES6 should not be Java, but sorting sets seems useful. – ericsoco Oct 12 '15 at 20:37
  • 1
    You can't `sort()` a `SortedSet` either. It uses a tree structure that allows in-order traversal, but you cannot *change* this ordering as with an `ArrayList`. If you want to change the order, use lists. – Has QUIT--Anony-Mousse Oct 12 '15 at 22:54
  • 1
    might need some research, but I think `Set` preserves order `new Set(['b', 'a', 'c'].sort())` – Slai Jul 29 '18 at 15:16
  • @Slai https://www.ecma-international.org/ecma-262/9.0/index.html#sec-set-objects I can not find any statement about the order of elements. – ceving Feb 25 '19 at 15:15
  • You can iterate through the elements of a set in insertion order. – Wojciech Bednarski Jul 25 '19 at 16:18
  • You’ve probably go the message that a set is a mathematical collection whose elements are unique and unordered. That JavaScript retains insertion order is an added feature. I guess the idea is that an Array is already ordered, so why would you order a set as well? In any case, you can construct the set by ordering the array first: `new Set(['b', 'a', 'c'].sort())` . – Manngo Oct 18 '19 at 05:56
  • See also the TC39 official discussion https://github.com/tc39/proposal-collection-methods/issues/12 – Yves M. May 27 '22 at 17:32

4 Answers4

209

A set is not an ordered abstract data structure.

A Set however always has the same iteration order - element insertion order [1], so when you iterate it (by an iterating method, by calling Symbol.iterator, or by a for.. of loop) you can always expect that.

You can always convert the set to an array and sort that.

Array.from(new Set(["b","a","c"])).sort();
[...(new Set(["b","a","c"]))].sort(); // with spread.

[1] forEach and CreateSetIterator

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 2
    Deleted my own answer that is the same as this, but will add back my comment: "I'm really hoping someone can provide an answer that doesn't require going from `Set` to `Array` and back. Seems pretty inefficient." – ericsoco Oct 12 '15 at 20:38
  • 15
    @ericsoco the ECMAScript spec does not specify an `OrderedSet` but a similar data structure exists in multiple other languages - typically implemented by a tree. You can use a userland collections library, namely - the popular ImmutableJS provides an [`OrderedSet`](https://facebook.github.io/immutable-js/docs/#/OrderedSet). Indeed that will likely be more efficient in a lot of cases. So if you have a set with over 100K elements - I'd definitely consider using an ordered set implementation instead. – Benjamin Gruenbaum Oct 12 '15 at 20:40
  • 9
    Please note that for set of numbers you have to give sort a comparison function: `(x, y) => x - y`. Your numbers will be sorted lexicographically otherwise. – neo Oct 09 '16 at 17:01
  • 2
    @BenjaminGruenbaum: I think you've misunderstood something; according to its documentation, Immutable.js's `OrderedSet` uses the *insertion order* as the iteration order; that is, it does the same thing that, according to your answer, the standard `Set` does. – ruakh Apr 02 '19 at 20:03
  • 3
    @ruakh However, an ImmutableJS `OrderedSet` object *does* allow you to sort() its contents, which a) the standard `Set` *does not*, and b) is the functionality OP wanted. – Jamie Ridding May 23 '20 at 02:20
  • 1
    @JamieRidding: OK, but I don't think that affects my comment at all? – ruakh May 23 '20 at 02:46
  • @ruakh, yeah you're right, might as well use the regular `Set` from immutable.js, which also provides a sort function: https://immutable-js.com/docs/v4.3.0/Set/#sort() – MazeChaZer Jul 24 '23 at 13:43
1

In some cases it may be preferable to "sort" the set in-place, similar to array.sort(), it can be done like this:

function sortSet(set) {
  const entries = [];
  for (const member of set) {
    entries.push(member);
  }
  set.clear();
  for (const entry of entries.sort()) {
    set.add(entry);
  }
  return set;
};

sortSet(new Set([3,2,1]))
// => Set(3) { 1, 2, 3 }
silverwind
  • 3,296
  • 29
  • 31
0

the simplest way to do it as.

console.log(new Set(['b', 'a', 'c'].sort()))
//Set(3) {"a", "b", "c"}
Taimoor Qureshi
  • 620
  • 4
  • 14
  • according to the question, his target was to retain the order of iteration in the set. it's not necessary to sort items in Set. – Taimoor Qureshi Sep 21 '21 at 15:16
0

The .sort function is a higher order function which means it can have another function inside. First of all only .sort() may work with characters or strings but it will give bugs for numbers. I have discussed sets in my video along with sort function. I hope you understand it. https://www.youtube.com/watch?v=ztw4Gh8eogw

//This is sort() for getting numbers in ascending order:
const setC = new Set(([58,12,11,10,5,32]).sort((a,b)=>a -b));
//This is sort() for getting numbers in descending order:
const setC = new Set(([58,12,11,10,5,32]).sort((a,b)=>b -a));
//This is sort() for strings
const setD=new Set((['mangoes','bananas', 'apples','oranages']).sort());
// This is sort() for characters
const setD=new Set((['m', 'b', 'a', 'r']).sort());
You can convert the set to an array too and then sort it but that is not 
required in your case.
const arrayofsetA = Array.from(setA);
//for strings or characters
arrayofsetA.sort();
//for numbers or floating point numbers
arrayofsetA.sort((a,b) => a-b);