diff --git a/packages/arrays/package.json b/packages/arrays/package.json index a442557404..52a60aa3b4 100644 --- a/packages/arrays/package.json +++ b/packages/arrays/package.json @@ -137,6 +137,9 @@ "./quicksort": { "default": "./quicksort.js" }, + "./rotate": { + "default": "./rotate.js" + }, "./shuffle": { "default": "./shuffle.js" }, diff --git a/packages/arrays/src/index.ts b/packages/arrays/src/index.ts index 8ca9104835..b92ece4e83 100644 --- a/packages/arrays/src/index.ts +++ b/packages/arrays/src/index.ts @@ -18,6 +18,7 @@ export * from "./iterator.js"; export * from "./levenshtein.js"; export * from "./peek.js"; export * from "./quicksort.js"; +export * from "./rotate.js"; export * from "./shuffle.js"; export * from "./sort-cached.js"; export * from "./starts-with.js"; diff --git a/packages/arrays/src/rotate.ts b/packages/arrays/src/rotate.ts new file mode 100644 index 0000000000..8f672b9dc6 --- /dev/null +++ b/packages/arrays/src/rotate.ts @@ -0,0 +1,46 @@ +import type { TypedArray } from "@thi.ng/api"; + +/** + * Rotates array by `num` items. If `num < 0` items are rotated left (towards + * the beginning of the array), otherwise to the right (end). The rotation + * distance will be `num % buf.length`. The function is no-op if the resulting + * distance is zero or `buf` is empty. + * + * @remarks + * Not suitable for typed arrays. Use {@link rotateTyped} for those. + * + * @param buf + * @param num + */ +export const rotate = (buf: Array, num: number) => { + if (!(num = __distance(buf, num))) return buf; + if (num < 0) { + buf.push(...buf.splice(0, -num)); + } else { + buf.unshift(...buf.splice(-num)); + } + return buf; +}; + +/** + * Same as {@link rotate}, but for optimized for typed arrays! + * + * @param buf + * @param num + */ +export const rotateTyped = (buf: TypedArray, num: number) => { + if (!(num = __distance(buf, num))) return buf; + if (num < 0) { + const tmp = buf.slice(0, -num); + buf.copyWithin(0, -num); + buf.set(tmp, buf.length + num); + } else if (num > 0) { + const tmp = buf.slice(buf.length - num); + buf.copyWithin(num, 0); + buf.set(tmp, 0); + } + return buf; +}; + +const __distance = (buf: ArrayLike, num: number) => + buf.length ? (num | 0) % buf.length : 0;