Skip to content

Commit

Permalink
feat(arrays): add types, quickSort(), swap(), multiSwap(), update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Oct 26, 2019
1 parent 37f411f commit b834722
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 4 deletions.
3 changes: 3 additions & 0 deletions packages/arrays/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@ import * as a from "@thi.ng/arrays";
- [ensureArray()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/ensure-array.ts)
- [ensureIterable()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/ensure-iterable.ts)
- [fuzzyMatch()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/fuzzy-match.ts)
- [multiSwap()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/swap.ts)
- [peek()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/peek.ts)
- [quickSort()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/quicksort.ts)
- [shuffle()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/shuffle.ts) (w/ custom PRNG support)
- [shuffleRange()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/shuffle.ts) (w/ custom PRNG support)
- [startsWith()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/starts-with.ts)
- [swap()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/swap.ts)
- [swizzle()](https://github.com/thi-ng/umbrella/tree/master/packages/arrays/src/swizzle.ts)

## Authors
Expand Down
5 changes: 5 additions & 0 deletions packages/arrays/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Fn3, TypedArray } from "@thi.ng/api";

export type AnyArray = any[] | TypedArray;

export type SwapFn = Fn3<AnyArray, number, number, void>;
5 changes: 2 additions & 3 deletions packages/arrays/src/ensure-iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import { illegalArgs } from "@thi.ng/errors";
*
* @param x
*/
export const ensureIterable = (x: any): IterableIterator<any> => {
if (!(x != null && x[Symbol.iterator])) {
export const ensureIterable = (x: any): Iterable<any> => {
(x == null || !x[Symbol.iterator]) &&
illegalArgs(`value is not iterable: ${x}`);
}
return x;
};
2 changes: 2 additions & 0 deletions packages/arrays/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export * from "./ensure-iterable";
export * from "./find";
export * from "./fuzzy-match";
export * from "./peek";
export * from "./quicksort";
export * from "./shuffle";
export * from "./starts-with";
export * from "./swap";
export * from "./swizzle";
64 changes: 64 additions & 0 deletions packages/arrays/src/quicksort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Comparator, Fn3, TypedArray } from "@thi.ng/api";
import { compare } from "@thi.ng/compare";
import { swap } from "./swap";

/**
* In-place quicksort implementation with optional comparator & index
* based swap function, useful for sorting multiple related arrays in
* parallel, based on a single sort criteria. Supports sorting of
* sub-ranges only, via optionally given `start`/`end` indices (both
* inclusive).
*
* Uses Hoare partitioning scheme. thi.ng/compare is used as default
* comparator and `swap` from this package as default swap function.
*
* https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
*
* ```
* a = [4, 3, 1, 8, 5]
* b = [40, 30, 10, 80, 50]
* c = [-4, -3, -1, -8, -5]
*
* // use `multiSwap` to sort extra arrays based on sort order of `a`
* quickSort(a, undefined, multiSwap(b, c))
* // [ 1, 3, 4, 5, 8 ] (a)
*
* b
* // [ 10, 30, 40, 50, 80 ]
* c
* // [ -1, -3, -4, -5, -8 ]
* ```
*
* @param arr
* @param _cmp
* @param _swap
* @param start
* @param end
*/
// prettier-ignore
export function quickSort<T>(arr: T[], _cmp?: Comparator<T>, _swap?: Fn3<T[], number, number, void>, start?: number, end?: number): T[];
// prettier-ignore
export function quickSort<T extends TypedArray>(arr: T, _cmp?: Comparator<number>, _swap?: Fn3<T, number, number, void>, start?: number, end?: number): T;
// prettier-ignore
export function quickSort(arr: any, _cmp: Comparator<any> = compare, _swap: Fn3<any, number, number, void> = swap, start = 0, end = arr.length - 1): any {
if (start < end) {
const pivot = arr[start + ((end - start) >> 1)];
let s = start - 1;
let e = end + 1;

while (true) {
do {
s++;
} while (_cmp(arr[s], pivot) < 0);
do {
e--;
} while (_cmp(arr[e], pivot) > 0);
if (s >= e) break;
_swap(arr, s, e);
}

quickSort(arr, _cmp, _swap, start, e);
quickSort(arr, _cmp, _swap, e + 1, end);
}
return arr;
}
3 changes: 2 additions & 1 deletion packages/arrays/src/shuffle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assert, TypedArray } from "@thi.ng/api";
import { IRandom, SYSTEM } from "@thi.ng/random";
import { AnyArray } from "./api";

/**
* Shuffles the items in the given index range of array `buf` using
Expand All @@ -10,7 +11,7 @@ import { IRandom, SYSTEM } from "@thi.ng/random";
* @param n
* @param rnd
*/
export const shuffleRange = <T extends any[] | TypedArray>(
export const shuffleRange = <T extends AnyArray>(
buf: T,
start = 0,
end = buf.length,
Expand Down
68 changes: 68 additions & 0 deletions packages/arrays/src/swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { AnyArray, SwapFn } from "./api";

/**
* Swaps values at index `x`/`y` in given array.
*
* @param arr
* @param x
* @param y
*/
export const swap = (arr: AnyArray, x: number, y: number) => {
const t = arr[x];
arr[x] = arr[y];
arr[y] = t;
};

/**
* Higher-order version of `swap` for swapping elements in multiple
* arrays at once. The returned function takes the same args as `swap`,
* and when called swaps 2 elements in the array given to that function
* AND in the arrays given to `multiSwap` itself. Provides fast routes
* for up to 3 extra arrays, then falls back to a loop-based approach.
*
* ```
* a = [2, 1];
* b = [20, 10];
* c = [40, 30];
*
* ms = multiSwap(b, c);
* ms(a, 0, 1);
*
* // a: [1, 2]
* // b: [10, 20]
* // c: [30, 40]
* ```
*
* @param xs
*/
export const multiSwap = (...xs: AnyArray[]): SwapFn => {
const [b, c, d] = xs;
const n = xs.length;
switch (n) {
case 0:
return swap;
case 1:
return (a, x, y) => {
swap(a, x, y);
swap(b, x, y);
};
case 2:
return (a, x, y) => {
swap(a, x, y);
swap(b, x, y);
swap(c, x, y);
};
case 3:
return (a, x, y) => {
swap(a, x, y);
swap(b, x, y);
swap(c, x, y);
swap(d, x, y);
};
default:
return (a, x, y) => {
swap(a, x, y);
for (let i = n; --i >= 0; ) swap(xs[i], x, y);
};
}
};

0 comments on commit b834722

Please sign in to comment.