-
-
Save vitaly-t/2c868874738cc966df776f383e5e0247 to your computer and use it in GitHub Desktop.
/** | |
* Iterable arrays chain, extended for "getLength" and "at" accessor. | |
*/ | |
export interface IArraysChain<T> extends RelativeIndexable<T>, Iterable<T> { | |
/** | |
* Calculates total length of all input arrays combined. | |
*/ | |
getLength(): number; | |
} | |
export function chainArrays(): IArraysChain<unknown>; | |
export function chainArrays<A>(a: ArrayLike<A>): IArraysChain<A>; | |
export function chainArrays<A, B>(a: ArrayLike<A>, b: ArrayLike<B>): IArraysChain<A | B>; | |
export function chainArrays<A, B, C>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>): IArraysChain<A | B | C>; | |
export function chainArrays<A, B, C, D>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>): IArraysChain<A | B | C | D>; | |
export function chainArrays<A, B, C, D, E>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>): IArraysChain<A | B | C | D | E>; | |
export function chainArrays<A, B, C, D, E, F>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>): IArraysChain<A | B | C | D | E | F>; | |
export function chainArrays<A, B, C, D, E, F, G>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>): IArraysChain<A | B | C | D | E | F | G>; | |
export function chainArrays<A, B, C, D, E, F, G, H>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>): IArraysChain<A | B | C | D | E | F | G | H>; | |
export function chainArrays<A, B, C, D, E, F, G, H, I>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>, i: ArrayLike<I>): IArraysChain<A | B | C | D | E | F | G | H | I>; | |
export function chainArrays<A, B, C, D, E, F, G, H, I, J>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>, i: ArrayLike<I>, j: ArrayLike<J>): IArraysChain<A | B | C | D | E | F | G | H | I | J>; | |
/** | |
* Logically concatenates arrays (chains them), into an iterable. | |
*/ | |
export function chainArrays<T>(...arr: Array<ArrayLike<T>>): IArraysChain<T> { | |
return { | |
getLength() { | |
return arr.reduce((a, c) => a + c.length, 0); | |
}, | |
at(i: number): T | undefined { | |
for (let j = 0; j < arr.length; j++) { | |
if (i < arr[j].length) { | |
return arr[j][i]; | |
} | |
i -= arr[j].length; | |
} | |
}, | |
[Symbol.iterator](): Iterator<T> { | |
let i = 0, k = -1, a: ArrayLike<T> = []; | |
return { | |
next(): IteratorResult<T> { | |
while (i === a.length) { | |
if (++k === arr.length) { | |
return {done: true, value: undefined}; | |
} | |
a = arr[k]; | |
i = 0; | |
} | |
return {done: false, value: a[i++]}; | |
} | |
}; | |
} | |
} | |
} | |
export function chainArraysReverse(): IArraysChain<unknown>; | |
export function chainArraysReverse<A>(a: ArrayLike<A>): IArraysChain<A>; | |
export function chainArraysReverse<A, B>(a: ArrayLike<A>, b: ArrayLike<B>): IArraysChain<A | B>; | |
export function chainArraysReverse<A, B, C>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>): IArraysChain<A | B | C>; | |
export function chainArraysReverse<A, B, C, D>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>): IArraysChain<A | B | C | D>; | |
export function chainArraysReverse<A, B, C, D, E>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>): IArraysChain<A | B | C | D | E>; | |
export function chainArraysReverse<A, B, C, D, E, F>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>): IArraysChain<A | B | C | D | E | F>; | |
export function chainArraysReverse<A, B, C, D, E, F, G>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>): IArraysChain<A | B | C | D | E | F | G>; | |
export function chainArraysReverse<A, B, C, D, E, F, G, H>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>): IArraysChain<A | B | C | D | E | F | G | H>; | |
export function chainArraysReverse<A, B, C, D, E, F, G, H, I>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>, i: ArrayLike<I>): IArraysChain<A | B | C | D | E | F | G | H | I>; | |
export function chainArraysReverse<A, B, C, D, E, F, G, H, I, J>(a: ArrayLike<A>, b: ArrayLike<B>, c: ArrayLike<C>, d: ArrayLike<D>, e: ArrayLike<E>, f: ArrayLike<F>, g: ArrayLike<G>, h: ArrayLike<H>, i: ArrayLike<I>, j: ArrayLike<J>): IArraysChain<A | B | C | D | E | F | G | H | I | J>; | |
/** | |
* Logically concatenates arrays (chains them), into a reversed iterable. | |
*/ | |
export function chainArraysReverse<T>(...arr: Array<ArrayLike<T>>): IArraysChain<T> { | |
return { | |
getLength() { | |
return arr.reduce((a, c) => a + c.length, 0); | |
}, | |
at(i: number): T | undefined { | |
for (let j = arr.length - 1; j >= 0; j--) { | |
if (i < arr[j].length) { | |
return arr[j][arr[j].length - (i + 1)]; | |
} | |
i -= arr[j].length; | |
} | |
}, | |
[Symbol.iterator](): Iterator<T> { | |
let i = -1, k = arr.length, a: ArrayLike<T>; | |
return { | |
next(): IteratorResult<T> { | |
while (i < 0) { | |
if (--k < 0) { | |
return {done: true, value: undefined}; | |
} | |
a = arr[k]; | |
i = a.length - 1; | |
} | |
return {done: false, value: a[i--]}; | |
} | |
}; | |
} | |
} | |
} |
@guest271314 This library is for dealing with large arrays, those are typically prepared for merging, not to be dynamically changing afterwords. If you for some odd reasons decide to do that anyway, then re-chain the arrays for the new length. Other than that, iteration is the main processing power here, which is the fastest, and it will work even with any such unexpected outside change.
I understand the intent. I merely illustrated a case where expectations can fail. It might be useful to point out the input Array
s are not expected to mutate for the expected result to be derived without any changes to the current implementation of the code.
UPDATE: method at
no longer depends on length
, and so it will work even if one of the input arrays changes. @guest271314
Best is to refer the repo from now on, which includes full test coverage.
@vitaly-t I've done this multiple ways; using various means. I think writing data to a contiguous block of memory can be simpler than trying to manage a superimposition of indexes over N Array
s. If that approach works for you, great. If you are interested in the different ways I've done this let me know and I'll share.
As there was no way to update "length" when a source array changes, without a super-slow proxy, I had to just replace length
with getLength
method.
This way, this code will retain its maximum performance.
Fails when original input
Array
length
changes