All files / collections / unstable_zip.ts

100.00% Branches 20/20
100.00% Functions 1/1
100.00% Lines 28/28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x4
x4
 
x21
x21
 
x20
 
x20
x21
x18
x18
x18
 
x20
 
 
x21
x14
x14
x14
x28
x28
x14
x14
 
x18
x13
x13
x23
x23
x13
x13
 
x6
x21




































































































// Copyright 2018-2026 the Deno authors. MIT license.
// This module is browser compatible.

/**
 * Builds N-tuples of elements from the given N iterables with matching
 * indices, stopping when the shortest iterable is exhausted.
 *
 * Notes:
 * - Inputs are consumed eagerly. Non-array iterables are materialized via
 *   `Array.from` before zipping, so passing an infinite iterable (such as an
 *   unbounded generator) will hang.
 * - Strings are iterables of code points. Passing a string zips it
 *   character-by-character rather than treating it as a single scalar value.
 *
 * @experimental **UNSTABLE**: New API, yet to be vetted.
 *
 * @typeParam T The tuple of element types in the input iterables.
 *
 * @param iterables The iterables to zip.
 *
 * @returns A new array containing N-tuples of elements from the given
 * iterables.
 *
 * @example Basic usage
 * ```ts
 * import { zip } from "@std/collections/unstable-zip";
 * import { assertEquals } from "@std/assert";
 *
 * const numbers = [1, 2, 3, 4];
 * const letters = ["a", "b", "c", "d"];
 * const pairs = zip(numbers, letters);
 *
 * assertEquals(
 *   pairs,
 *   [
 *     [1, "a"],
 *     [2, "b"],
 *     [3, "c"],
 *     [4, "d"],
 *   ],
 * );
 * ```
 *
 * @example With iterables
 * ```ts
 * import { zip } from "@std/collections/unstable-zip";
 * import { assertEquals } from "@std/assert";
 *
 * assertEquals(
 *   zip(new Set([1, 2, 3]), ["a", "b", "c"]),
 *   [[1, "a"], [2, "b"], [3, "c"]],
 * );
 * ```
 *
 * @example Strings are iterables
 * ```ts
 * import { zip } from "@std/collections/unstable-zip";
 * import { assertEquals } from "@std/assert";
 *
 * // A string is zipped character-by-character, not as a single value.
 * assertEquals(
 *   zip("abc", [1, 2, 3]),
 *   [["a", 1], ["b", 2], ["c", 3]],
 * );
 * ```
 */
export function zip<T extends unknown[]>(
  ...iterables: { [K in keyof T]: Iterable<T[K]> }
): T[] {
  const arrayCount = iterables.length;
  if (arrayCount === 0) return [];

  const arrays = iterables.map((it) => Array.isArray(it) ? it : Array.from(it));

  let minLength = arrays[0]!.length;
  for (let i = 1; i < arrayCount; ++i) {
    const len = arrays[i]!.length;
    if (len < minLength) minLength = len;
  }

  const result: T[] = new Array(minLength);

  // Fast path for two iterables
  if (arrayCount === 2) {
    const a = arrays[0]!;
    const b = arrays[1]!;
    for (let i = 0; i < minLength; ++i) {
      result[i] = [a[i], b[i]] as T;
    }
    return result;
  }

  for (let i = 0; i < minLength; ++i) {
    const tuple: unknown[] = new Array(arrayCount);
    for (let j = 0; j < arrayCount; ++j) {
      tuple[j] = arrays[j]![i];
    }
    result[i] = tuple as T;
  }

  return result;
}