Browse Source

Add support for forward (#100)

Christopher Chedeau 5 years ago
parent
commit
6b8d2970ac
2 changed files with 148 additions and 2 deletions
  1. 52 2
      src/zindex.test.ts
  2. 96 0
      src/zindex.ts

+ 52 - 2
src/zindex.test.ts

@@ -1,6 +1,11 @@
-import { moveOneLeft, moveAllLeft } from "./zindex";
+import { moveOneLeft, moveOneRight, moveAllLeft, moveAllRight } from "./zindex";
 
-function expectMove(fn, elems, indices, equal) {
+function expectMove<T>(
+  fn: (elements: T[], indicesToMove: number[]) => void,
+  elems: T[],
+  indices: number[],
+  equal: T[]
+) {
   fn(elems, indices);
   expect(elems).toEqual(equal);
 }
@@ -17,6 +22,18 @@ it("should moveOneLeft", () => {
   expectMove(moveOneLeft, ["a", "b", "c", "d"], [1, 3], ["b", "a", "d", "c"]);
 });
 
+it("should moveOneRight", () => {
+  expectMove(moveOneRight, ["a", "b", "c", "d"], [1, 2], ["a", "d", "b", "c"]);
+  expectMove(moveOneRight, ["a", "b", "c", "d"], [3], ["a", "b", "c", "d"]);
+  expectMove(
+    moveOneRight,
+    ["a", "b", "c", "d"],
+    [0, 1, 2, 3],
+    ["a", "b", "c", "d"]
+  );
+  expectMove(moveOneRight, ["a", "b", "c", "d"], [0, 2], ["b", "a", "d", "c"]);
+});
+
 it("should moveAllLeft", () => {
   expectMove(
     moveAllLeft,
@@ -49,3 +66,36 @@ it("should moveAllLeft", () => {
     ["e", "f", "g", "a", "b", "c", "d"]
   );
 });
+
+it("should moveAllRight", () => {
+  expectMove(
+    moveAllRight,
+    ["a", "b", "c", "d", "e", "f", "g"],
+    [2, 5],
+    ["a", "b", "d", "e", "g", "c", "f"]
+  );
+  expectMove(
+    moveAllRight,
+    ["a", "b", "c", "d", "e", "f", "g"],
+    [5],
+    ["a", "b", "c", "d", "e", "g", "f"]
+  );
+  expectMove(
+    moveAllRight,
+    ["a", "b", "c", "d", "e", "f", "g"],
+    [0, 1, 2, 3, 4, 5, 6],
+    ["a", "b", "c", "d", "e", "f", "g"]
+  );
+  expectMove(
+    moveAllRight,
+    ["a", "b", "c", "d", "e", "f", "g"],
+    [0, 1, 2],
+    ["d", "e", "f", "g", "a", "b", "c"]
+  );
+  expectMove(
+    moveAllRight,
+    ["a", "b", "c", "d", "e", "f", "g"],
+    [4, 5, 6],
+    ["a", "b", "c", "d", "e", "f", "g"]
+  );
+});

+ 96 - 0
src/zindex.ts

@@ -19,6 +19,24 @@ export function moveOneLeft<T>(elements: T[], indicesToMove: number[]) {
   });
 }
 
+export function moveOneRight<T>(elements: T[], indicesToMove: number[]) {
+  const reversedIndicesToMove = indicesToMove.sort(
+    (a: number, b: number) => b - a
+  );
+  let isSorted = true;
+
+  // We go from right to left to avoid overriding the wrong elements
+  reversedIndicesToMove.forEach((index, i) => {
+    // We don't want to bubble the first elements that are sorted as they are
+    // already in their correct position
+    isSorted = isSorted && index === elements.length - i - 1;
+    if (isSorted) {
+      return;
+    }
+    swap(elements, index + 1, index);
+  });
+}
+
 // Let's go through an example
 //        |        |
 // [a, b, c, d, e, f, g]
@@ -95,3 +113,81 @@ export function moveAllLeft<T>(elements: T[], indicesToMove: number[]) {
     elements[i] = element;
   });
 }
+
+// Let's go through an example
+//        |        |
+// [a, b, c, d, e, f, g]
+// -->
+// [a, b, d, e, g, c, f]
+//
+// We are going to override all the elements we want to move, so we keep them in an array
+// that we will restore at the end.
+// [c, f]
+//
+// From now on, we'll never read those values from the array anymore
+//        |0       |1
+// [a, b, _, d, e, _, g]
+//
+// The idea is that we want to shift all the elements between the marker 0 and 1
+// by one slot to the left.
+//
+//        |0       |1
+// [a, b, _, d, e, _, g]
+//          <- <-
+//
+// which gives us
+//
+//        |0       |1
+// [a, b, d, e, _, _, g]
+//
+// Now, we need to move all the elements from marker 1 to the end by two (not one)
+// slots to the left, which gives us
+//
+//        |0       |1
+// [a, b, d, e, _, _, g]
+//              ^------
+//
+// which gives us
+//
+//        |0       |1
+// [a, b, d, e, g, _, _]
+//
+// At this point, we can fill back the rightmost elements with the array we saved at
+// the beggining
+//
+//        |0       |1
+// [a, b, d, e, g, c, f]
+//
+// And we are done!
+export function moveAllRight<T>(elements: T[], indicesToMove: number[]) {
+  const reversedIndicesToMove = indicesToMove.sort(
+    (a: number, b: number) => b - a
+  );
+
+  // Copy the elements to move
+  const rightMostElements = reversedIndicesToMove.map(index => elements[index]);
+
+  indicesToMove = reversedIndicesToMove
+    // We go from left to right to avoid overriding elements.
+    .reverse()
+    // We last element index for the final marker
+    .concat([elements.length]);
+
+  indicesToMove.forEach((index, i) => {
+    // We skip the first one as it is not paired with anything else
+    if (i === 0) {
+      return;
+    }
+
+    // We go from the next marker to the left (i - 1) to the current one (index)
+    for (let pos = indicesToMove[i - 1] + 1; pos < index; ++pos) {
+      // We move by 1 the first time, 2 the second... So we can use the index i in the array
+      elements[pos - i] = elements[pos];
+    }
+  });
+
+  // The final step
+  rightMostElements.forEach((element, i) => {
+    elements[elements.length - i - 1] = element;
+  });
+}