stable.js 2.9 KB

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