Last active
July 21, 2023 22:47
-
-
Save sebinsua/e756554faaa2d346aff0341374ee792e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type _Tuple< | |
T, | |
N extends number, | |
R extends readonly T[] = [] | |
> = R["length"] extends N ? R : _Tuple<T, N, readonly [T, ...R]>; | |
type Tuple<T, N extends number> = _Tuple<T, N> & { | |
readonly length: N; | |
[I: number]: T; | |
[Symbol.iterator]: () => IterableIterator<T>; | |
}; | |
function zip< | |
A extends readonly any[], | |
Length extends A["length"], | |
B extends Tuple<any, Length> | |
>(a: A, b: B): Tuple<readonly [A[number], B[number]], Length> { | |
if (a.length !== b.length) { | |
throw new Error(`zip cannot operate on different length arrays; ${a.length} !== ${b.length}`); | |
} | |
return a.map((v, index) => [v, b[index]]) as Tuple< | |
readonly [A[number], B[number]], | |
Length | |
>; | |
} | |
const a = [1, 2, 3] as const; | |
const b1 = [1, 2, 6, 2, 4] as const; | |
const b2 = [1, 2, 6] as const; | |
// @ts-expect-error Source has 5 element(s) but target allows only 3. | |
const c1 = zip(a, b1); | |
const c2 = zip(a, b2); | |
console.log(c2); | |
// ^? |
type Zipped<A extends readonly (readonly any[])[]> = {
[RowIndex in keyof A[0]]: RowIndex extends "length" ? number : {
[ArrayIndex in keyof A]: A[ArrayIndex] extends readonly any[] ? A[ArrayIndex][RowIndex] : never;
};
};
type NumericRange<TotalLength extends number, TempRange extends any[] = []> =
TempRange['length'] extends TotalLength ? TempRange : NumericRange<TotalLength, [...TempRange, TempRange['length']]>;
type TupleFromKeys<T, K extends readonly (keyof T)[]> = {
[I in keyof K]: K[I] extends keyof T ? T[K[I]] : never;
};
type Tuple<T extends readonly (readonly any[])[]> = TupleFromKeys<Zipped<T>, NumericRange<T[0]['length']>>;
function zip<const T extends readonly (readonly any[])[]>(...arrays: T): Tuple<T> {
const minLength = Math.min(...arrays.map(arr => arr.length));
const result: any[][] = [];
for (let i = 0; i < minLength; i++) {
const zippedItem = arrays.map(arr => arr[i]);
result.push(zippedItem);
}
return result as Tuple<T>;
}
const grid = [
['a', 1, true],
['b', 2, false],
['c', 3, true]
] as const;
const [col1, col2, col3] = zip(...grid);
console.log(col1);
console.log(col2);
console.log(col3);
^ This approach would be useful if you needed to convert a grid of rows into a list of columns.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I improved upon this and wrote it up as a blog post here: https://twitter.com/sebinsua/status/1656297294008819712