zindex.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. function swap<T>(elements: T[], indexA: number, indexB: number) {
  2. const element = elements[indexA];
  3. elements[indexA] = elements[indexB];
  4. elements[indexB] = element;
  5. }
  6. export function moveOneLeft<T>(elements: T[], indicesToMove: number[]) {
  7. indicesToMove.sort((a: number, b: number) => a - b);
  8. let isSorted = true;
  9. // We go from left to right to avoid overriding the wrong elements
  10. indicesToMove.forEach((index, i) => {
  11. // We don't want to bubble the first elements that are sorted as they are
  12. // already in their correct position
  13. isSorted = isSorted && index === i;
  14. if (isSorted) {
  15. return;
  16. }
  17. swap(elements, index - 1, index);
  18. });
  19. return elements;
  20. }
  21. export function moveOneRight<T>(elements: T[], indicesToMove: number[]) {
  22. const reversedIndicesToMove = indicesToMove.sort(
  23. (a: number, b: number) => b - a,
  24. );
  25. let isSorted = true;
  26. // We go from right to left to avoid overriding the wrong elements
  27. reversedIndicesToMove.forEach((index, i) => {
  28. // We don't want to bubble the first elements that are sorted as they are
  29. // already in their correct position
  30. isSorted = isSorted && index === elements.length - i - 1;
  31. if (isSorted) {
  32. return;
  33. }
  34. swap(elements, index + 1, index);
  35. });
  36. return elements;
  37. }
  38. // Let's go through an example
  39. // | |
  40. // [a, b, c, d, e, f, g]
  41. // -->
  42. // [c, f, a, b, d, e, g]
  43. //
  44. // We are going to override all the elements we want to move, so we keep them in an array
  45. // that we will restore at the end.
  46. // [c, f]
  47. //
  48. // From now on, we'll never read those values from the array anymore
  49. // |1 |0
  50. // [a, b, _, d, e, _, g]
  51. //
  52. // The idea is that we want to shift all the elements between the marker 0 and 1
  53. // by one slot to the right.
  54. //
  55. // |1 |0
  56. // [a, b, _, d, e, _, g]
  57. // -> ->
  58. //
  59. // which gives us
  60. //
  61. // |1 |0
  62. // [a, b, _, _, d, e, g]
  63. //
  64. // Now, we need to move all the elements from marker 1 to the beginning by two (not one)
  65. // slots to the right, which gives us
  66. //
  67. // |1 |0
  68. // [a, b, _, _, d, e, g]
  69. // ---|--^ ^
  70. // ------|
  71. //
  72. // which gives us
  73. //
  74. // |1 |0
  75. // [_, _, a, b, d, e, g]
  76. //
  77. // At this point, we can fill back the leftmost elements with the array we saved at
  78. // the beginning
  79. //
  80. // |1 |0
  81. // [c, f, a, b, d, e, g]
  82. //
  83. // And we are done!
  84. export function moveAllLeft<T>(elements: T[], indicesToMove: number[]) {
  85. indicesToMove.sort((a: number, b: number) => a - b);
  86. // Copy the elements to move
  87. const leftMostElements = indicesToMove.map(index => elements[index]);
  88. const reversedIndicesToMove = indicesToMove
  89. // We go from right to left to avoid overriding elements.
  90. .reverse()
  91. // We add 0 for the final marker
  92. .concat([0]);
  93. reversedIndicesToMove.forEach((index, i) => {
  94. // We skip the first one as it is not paired with anything else
  95. if (i === 0) {
  96. return;
  97. }
  98. // We go from the next marker to the right (i - 1) to the current one (index)
  99. for (let pos = reversedIndicesToMove[i - 1] - 1; pos >= index; --pos) {
  100. // We move by 1 the first time, 2 the second... So we can use the index i in the array
  101. elements[pos + i] = elements[pos];
  102. }
  103. });
  104. // The final step
  105. leftMostElements.forEach((element, i) => {
  106. elements[i] = element;
  107. });
  108. return elements;
  109. }
  110. // Let's go through an example
  111. // | |
  112. // [a, b, c, d, e, f, g]
  113. // -->
  114. // [a, b, d, e, g, c, f]
  115. //
  116. // We are going to override all the elements we want to move, so we keep them in an array
  117. // that we will restore at the end.
  118. // [c, f]
  119. //
  120. // From now on, we'll never read those values from the array anymore
  121. // |0 |1
  122. // [a, b, _, d, e, _, g]
  123. //
  124. // The idea is that we want to shift all the elements between the marker 0 and 1
  125. // by one slot to the left.
  126. //
  127. // |0 |1
  128. // [a, b, _, d, e, _, g]
  129. // <- <-
  130. //
  131. // which gives us
  132. //
  133. // |0 |1
  134. // [a, b, d, e, _, _, g]
  135. //
  136. // Now, we need to move all the elements from marker 1 to the end by two (not one)
  137. // slots to the left, which gives us
  138. //
  139. // |0 |1
  140. // [a, b, d, e, _, _, g]
  141. // ^------
  142. //
  143. // which gives us
  144. //
  145. // |0 |1
  146. // [a, b, d, e, g, _, _]
  147. //
  148. // At this point, we can fill back the rightmost elements with the array we saved at
  149. // the beginning
  150. //
  151. // |0 |1
  152. // [a, b, d, e, g, c, f]
  153. //
  154. // And we are done!
  155. export function moveAllRight<T>(elements: T[], indicesToMove: number[]) {
  156. const reversedIndicesToMove = indicesToMove.sort(
  157. (a: number, b: number) => b - a,
  158. );
  159. // Copy the elements to move
  160. const rightMostElements = reversedIndicesToMove.map(index => elements[index]);
  161. indicesToMove = reversedIndicesToMove
  162. // We go from left to right to avoid overriding elements.
  163. .reverse()
  164. // We last element index for the final marker
  165. .concat([elements.length]);
  166. indicesToMove.forEach((index, i) => {
  167. // We skip the first one as it is not paired with anything else
  168. if (i === 0) {
  169. return;
  170. }
  171. // We go from the next marker to the left (i - 1) to the current one (index)
  172. for (let pos = indicesToMove[i - 1] + 1; pos < index; ++pos) {
  173. // We move by 1 the first time, 2 the second... So we can use the index i in the array
  174. elements[pos - i] = elements[pos];
  175. }
  176. });
  177. // The final step
  178. rightMostElements.forEach((element, i) => {
  179. elements[elements.length - i - 1] = element;
  180. });
  181. return elements;
  182. }