|
1 | 1 | /** |
2 | 2 | * @author Richard Davey <rich@photonstorm.com> |
| 3 | + * @author Angry Bytes (and contributors) |
3 | 4 | * @copyright 2020 Photon Storm Ltd. |
4 | 5 | * @license {@link https://opensource.org/licenses/MIT|MIT License} |
5 | 6 | */ |
6 | 7 |
|
7 | | -//! stable.js 0.1.6, https://github.com/Two-Screen/stable |
8 | | -//! © 2017 Angry Bytes and contributors. MIT licensed. |
9 | | - |
10 | 8 | /** |
11 | | - * @namespace Phaser.Utils.Array.StableSortFunctions |
12 | | - */ |
13 | | - |
14 | | -(function() { |
15 | | - |
16 | | - /** |
17 | | - * A stable array sort, because `Array#sort()` is not guaranteed stable. |
18 | | - * This is an implementation of merge sort, without recursion. |
| 9 | + * The comparator function. |
19 | 10 | * |
20 | | - * @function Phaser.Utils.Array.StableSort |
21 | | - * @since 3.0.0 |
| 11 | + * @ignore |
22 | 12 | * |
23 | | - * @param {array} arr - The input array to be sorted. |
24 | | - * @param {function} comp - The comparison handler. |
| 13 | + * @param {*} a - The first item to test. |
| 14 | + * @param {*} b - The second itemt to test. |
25 | 15 | * |
26 | | - * @return {array} The sorted result. |
| 16 | + * @return {boolean} True if they localCompare, otherwise false. |
27 | 17 | */ |
28 | | -var stable = function(arr, comp) { |
29 | | - return exec(arr.slice(), comp); |
30 | | -}; |
| 18 | +function Compare (a, b) |
| 19 | +{ |
| 20 | + return String(a).localeCompare(b); |
| 21 | +} |
31 | 22 |
|
32 | | - /** |
33 | | - * Sort the input array and simply copy it back if the result isn't in the original array, which happens on an odd number of passes. |
| 23 | +/** |
| 24 | + * Process the array contents. |
34 | 25 | * |
35 | | - * @function Phaser.Utils.Array.StableSortFunctions.inplace |
36 | | - * @memberof Phaser.Utils.Array.StableSortFunctions |
37 | | - * @since 3.0.0 |
38 | 26 | * @ignore |
39 | 27 | * |
40 | | - * @param {array} arr - The input array. |
41 | | - * @param {function} comp - The comparison handler. |
| 28 | + * @param {array} array - The array to process. |
| 29 | + * @param {function} compare - The comparison function. |
42 | 30 | * |
43 | | - * @return {array} The sorted array. |
| 31 | + * @return {array} - The processed array. |
44 | 32 | */ |
45 | | -stable.inplace = function(arr, comp) { |
46 | | - var result = exec(arr, comp); |
47 | | - |
48 | | - // This simply copies back if the result isn't in the original array, |
49 | | - // which happens on an odd number of passes. |
50 | | - if (result !== arr) { |
51 | | - pass(result, null, arr.length, arr); |
52 | | - } |
53 | | - |
54 | | - return arr; |
55 | | -}; |
56 | | - |
57 | | -// Execute the sort using the input array and a second buffer as work space. |
58 | | -// Returns one of those two, containing the final result. |
59 | | -function exec(arr, comp) { |
60 | | - if (typeof(comp) !== 'function') { |
61 | | - comp = function(a, b) { |
62 | | - return String(a).localeCompare(b); |
63 | | - }; |
64 | | - } |
65 | | - |
| 33 | +function Process (array, compare) |
| 34 | +{ |
66 | 35 | // Short-circuit when there's nothing to sort. |
67 | | - var len = arr.length; |
68 | | - if (len <= 1) { |
69 | | - return arr; |
| 36 | + var len = array.length; |
| 37 | + |
| 38 | + if (len <= 1) |
| 39 | + { |
| 40 | + return array; |
70 | 41 | } |
71 | 42 |
|
72 | 43 | // Rather than dividing input, simply iterate chunks of 1, 2, 4, 8, etc. |
73 | 44 | // Chunks are the size of the left or right hand in merge sort. |
74 | 45 | // Stop when the left-hand covers all of the array. |
75 | 46 | var buffer = new Array(len); |
76 | | - for (var chk = 1; chk < len; chk *= 2) { |
77 | | - pass(arr, comp, chk, buffer); |
78 | 47 |
|
79 | | - var tmp = arr; |
80 | | - arr = buffer; |
| 48 | + for (var chk = 1; chk < len; chk *= 2) |
| 49 | + { |
| 50 | + RunPass(array, compare, chk, buffer); |
| 51 | + |
| 52 | + var tmp = array; |
| 53 | + |
| 54 | + array = buffer; |
| 55 | + |
81 | 56 | buffer = tmp; |
82 | 57 | } |
83 | 58 |
|
84 | | - return arr; |
| 59 | + return array; |
85 | 60 | } |
86 | 61 |
|
87 | | -// Run a single pass with the given chunk size. |
88 | | -var pass = function(arr, comp, chk, result) { |
| 62 | +/** |
| 63 | + * Run a single pass with the given chunk size. |
| 64 | + * |
| 65 | + * @ignore |
| 66 | + * |
| 67 | + * @param {array} arr - The array to run the pass on. |
| 68 | + * @param {function} comp - The comparison function. |
| 69 | + * @param {number} chk - The number of iterations. |
| 70 | + * @param {array} result - The array to store the result in. |
| 71 | + */ |
| 72 | +function RunPass (arr, comp, chk, result) |
| 73 | +{ |
89 | 74 | var len = arr.length; |
90 | 75 | var i = 0; |
| 76 | + |
91 | 77 | // Step size / double chunk size. |
92 | 78 | var dbl = chk * 2; |
| 79 | + |
93 | 80 | // Bounds of the left and right chunks. |
94 | 81 | var l, r, e; |
| 82 | + |
95 | 83 | // Iterators over the left and right chunk. |
96 | 84 | var li, ri; |
97 | 85 |
|
98 | 86 | // Iterate over pairs of chunks. |
99 | | - for (l = 0; l < len; l += dbl) { |
| 87 | + for (l = 0; l < len; l += dbl) |
| 88 | + { |
100 | 89 | r = l + chk; |
101 | 90 | e = r + chk; |
102 | | - if (r > len) r = len; |
103 | | - if (e > len) e = len; |
| 91 | + |
| 92 | + if (r > len) |
| 93 | + { |
| 94 | + r = len; |
| 95 | + } |
| 96 | + |
| 97 | + if (e > len) |
| 98 | + { |
| 99 | + e = len; |
| 100 | + } |
104 | 101 |
|
105 | 102 | // Iterate both chunks in parallel. |
106 | 103 | li = l; |
107 | 104 | ri = r; |
108 | | - while (true) { |
| 105 | + |
| 106 | + while (true) |
| 107 | + { |
109 | 108 | // Compare the chunks. |
110 | | - if (li < r && ri < e) { |
| 109 | + if (li < r && ri < e) |
| 110 | + { |
111 | 111 | // This works for a regular `sort()` compatible comparator, |
112 | 112 | // but also for a simple comparator like: `a > b` |
113 | | - if (comp(arr[li], arr[ri]) <= 0) { |
| 113 | + if (comp(arr[li], arr[ri]) <= 0) |
| 114 | + { |
114 | 115 | result[i++] = arr[li++]; |
115 | 116 | } |
116 | | - else { |
| 117 | + else |
| 118 | + { |
117 | 119 | result[i++] = arr[ri++]; |
118 | 120 | } |
119 | 121 | } |
120 | | - // Nothing to compare, just flush what's left. |
121 | | - else if (li < r) { |
| 122 | + else if (li < r) |
| 123 | + { |
| 124 | + // Nothing to compare, just flush what's left. |
122 | 125 | result[i++] = arr[li++]; |
123 | 126 | } |
124 | | - else if (ri < e) { |
| 127 | + else if (ri < e) |
| 128 | + { |
125 | 129 | result[i++] = arr[ri++]; |
126 | 130 | } |
127 | | - // Both iterators are at the chunk ends. |
128 | | - else { |
| 131 | + else |
| 132 | + { |
| 133 | + // Both iterators are at the chunk ends. |
129 | 134 | break; |
130 | 135 | } |
131 | 136 | } |
132 | 137 | } |
133 | | -}; |
134 | | - |
135 | | -// Export using CommonJS or to the window. |
136 | | -if (typeof(module) !== 'undefined') { |
137 | | - module.exports = stable; |
138 | | -} |
139 | | -else { |
140 | | - window.stable = stable; |
141 | 138 | } |
142 | 139 |
|
143 | | -})(); |
| 140 | +/** |
| 141 | + * An in-place stable array sort, because `Array#sort()` is not guaranteed stable. |
| 142 | + * |
| 143 | + * This is an implementation of merge sort, without recursion. |
| 144 | + * |
| 145 | + * Function based on the Two-Screen/stable sort 0.1.8 from https://github.com/Two-Screen/stable |
| 146 | + * |
| 147 | + * @function Phaser.Utils.Array.StableSort |
| 148 | + * @since 3.0.0 |
| 149 | + * |
| 150 | + * @param {array} array - The input array to be sorted. |
| 151 | + * @param {function} [compare] - The comparison function. |
| 152 | + * |
| 153 | + * @return {array} The sorted result. |
| 154 | + */ |
| 155 | +var StableSort = function (array, compare) |
| 156 | +{ |
| 157 | + if (compare === undefined) { compare = Compare; } |
| 158 | + |
| 159 | + var result = Process(array, compare); |
| 160 | + |
| 161 | + // This simply copies back if the result isn't in the original array, which happens on an odd number of passes. |
| 162 | + if (result !== array) |
| 163 | + { |
| 164 | + RunPass(result, null, array.length, array); |
| 165 | + } |
| 166 | + |
| 167 | + return array; |
| 168 | +}; |
| 169 | + |
| 170 | +module.exports = StableSort; |
0 commit comments