From 6b8d2970acdc8f59f8990b7cf28de09f863e31bb Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Fri, 3 Jan 2020 21:38:48 -0800 Subject: [PATCH] Add support for forward (#100) --- src/zindex.test.ts | 54 +++++++++++++++++++++++++- src/zindex.ts | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/zindex.test.ts b/src/zindex.test.ts index dbc66e47..c85b0c87 100644 --- a/src/zindex.test.ts +++ b/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( + 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"] + ); +}); diff --git a/src/zindex.ts b/src/zindex.ts index a3ed35df..9ea5850b 100644 --- a/src/zindex.ts +++ b/src/zindex.ts @@ -19,6 +19,24 @@ export function moveOneLeft(elements: T[], indicesToMove: number[]) { }); } +export function moveOneRight(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(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(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; + }); +}