1+ //! stable.js 0.1.6, https://github.com/Two-Screen/stable
2+ //! © 2017 Angry Bytes and contributors. MIT licensed.
3+
4+ ( function ( ) {
5+
6+ // A stable array sort, because `Array#sort()` is not guaranteed stable.
7+ // This is an implementation of merge sort, without recursion.
8+
9+ var stable = function ( arr , comp ) {
10+ return exec ( arr . slice ( ) , comp ) ;
11+ } ;
12+
13+ stable . inplace = function ( arr , comp ) {
14+ var result = exec ( arr , comp ) ;
15+
16+ // This simply copies back if the result isn't in the original array,
17+ // which happens on an odd number of passes.
18+ if ( result !== arr ) {
19+ pass ( result , null , arr . length , arr ) ;
20+ }
21+
22+ return arr ;
23+ } ;
24+
25+ // Execute the sort using the input array and a second buffer as work space.
26+ // Returns one of those two, containing the final result.
27+ function exec ( arr , comp ) {
28+ if ( typeof ( comp ) !== 'function' ) {
29+ comp = function ( a , b ) {
30+ return String ( a ) . localeCompare ( b ) ;
31+ } ;
32+ }
33+
34+ // Short-circuit when there's nothing to sort.
35+ var len = arr . length ;
36+ if ( len <= 1 ) {
37+ return arr ;
38+ }
39+
40+ // Rather than dividing input, simply iterate chunks of 1, 2, 4, 8, etc.
41+ // Chunks are the size of the left or right hand in merge sort.
42+ // Stop when the left-hand covers all of the array.
43+ var buffer = new Array ( len ) ;
44+ for ( var chk = 1 ; chk < len ; chk *= 2 ) {
45+ pass ( arr , comp , chk , buffer ) ;
46+
47+ var tmp = arr ;
48+ arr = buffer ;
49+ buffer = tmp ;
50+ }
51+
52+ return arr ;
53+ }
54+
55+ // Run a single pass with the given chunk size.
56+ var pass = function ( arr , comp , chk , result ) {
57+ var len = arr . length ;
58+ var i = 0 ;
59+ // Step size / double chunk size.
60+ var dbl = chk * 2 ;
61+ // Bounds of the left and right chunks.
62+ var l , r , e ;
63+ // Iterators over the left and right chunk.
64+ var li , ri ;
65+
66+ // Iterate over pairs of chunks.
67+ for ( l = 0 ; l < len ; l += dbl ) {
68+ r = l + chk ;
69+ e = r + chk ;
70+ if ( r > len ) r = len ;
71+ if ( e > len ) e = len ;
72+
73+ // Iterate both chunks in parallel.
74+ li = l ;
75+ ri = r ;
76+ while ( true ) {
77+ // Compare the chunks.
78+ if ( li < r && ri < e ) {
79+ // This works for a regular `sort()` compatible comparator,
80+ // but also for a simple comparator like: `a > b`
81+ if ( comp ( arr [ li ] , arr [ ri ] ) <= 0 ) {
82+ result [ i ++ ] = arr [ li ++ ] ;
83+ }
84+ else {
85+ result [ i ++ ] = arr [ ri ++ ] ;
86+ }
87+ }
88+ // Nothing to compare, just flush what's left.
89+ else if ( li < r ) {
90+ result [ i ++ ] = arr [ li ++ ] ;
91+ }
92+ else if ( ri < e ) {
93+ result [ i ++ ] = arr [ ri ++ ] ;
94+ }
95+ // Both iterators are at the chunk ends.
96+ else {
97+ break ;
98+ }
99+ }
100+ }
101+ } ;
102+
103+ // Export using CommonJS or to the window.
104+ if ( typeof ( module ) !== 'undefined' ) {
105+ module . exports = stable ;
106+ }
107+ else {
108+ window . stable = stable ;
109+ }
110+
111+ } ) ( ) ;
0 commit comments