-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from Baek2back/itertools/basic
Itertools/basic
- Loading branch information
Showing
15 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@iter-x/itertools": patch | ||
--- | ||
|
||
implement basic operation of itertools |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { accumulate } from "./accumulate"; | ||
import { take } from "./take"; | ||
|
||
describe("accumulate", () => { | ||
it("should accumulate values from iterator", () => { | ||
const iterable = accumulate([1, 2, 3], (x, y) => x + y); | ||
|
||
expect(take(Number.POSITIVE_INFINITY, iterable)).toEqual([1, 3, 6]); | ||
}); | ||
|
||
it("should accumulate values from iterator with initial value", () => { | ||
const iterable = accumulate([1, 2, 3, 4, 5], (a, b) => a + b, 100); | ||
|
||
expect(take(Number.POSITIVE_INFINITY, iterable)).toEqual([ | ||
100, 101, 103, 106, 110, 115, | ||
]); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
type Reducer<T, U> = (acc: U, item: T) => U; | ||
|
||
/** | ||
* | ||
* @example | ||
* ```typescript | ||
* accumulate([1,2,3,4,5], (a, b) => a + b, 100) // => 100 101 103 106 110 115 | ||
* accumulate([1,2,3,4,5], (a, b) => a * b) // => 1 2 6 24 120 | ||
* ``` | ||
*/ | ||
|
||
export function* accumulate<T, U = T>( | ||
iterable: Iterable<T>, | ||
func: Reducer<T, U>, | ||
initial?: U, | ||
): IterableIterator<U> { | ||
const iterator = iterable[Symbol.iterator](); | ||
let accumulator: U; | ||
|
||
if (initial === undefined) { | ||
const first = iterator.next(); | ||
if (first.done) return; | ||
accumulator = first.value as unknown as U; | ||
} else { | ||
accumulator = initial; | ||
} | ||
|
||
yield accumulator; | ||
|
||
while (true) { | ||
const item = iterator.next(); | ||
if (item.done) break; | ||
accumulator = func(accumulator, item.value); | ||
yield accumulator; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { batched } from "./batched"; | ||
import { take } from "./take"; | ||
|
||
describe("batched", () => { | ||
it("returns remainder in final batch", () => { | ||
const result = take( | ||
Number.POSITIVE_INFINITY, | ||
batched([1, 1, 1, 1, 1, 1, 2, 2], 3), | ||
); | ||
expect(result).toEqual([ | ||
[1, 1, 1], | ||
[1, 1, 1], | ||
[2, 2], | ||
]); | ||
}); | ||
|
||
it("throw error when batch length does not matched n with strict option", () => { | ||
expect(() => { | ||
take( | ||
Number.POSITIVE_INFINITY, | ||
batched([1, 1, 1, 1, 1, 1, 2, 2], 3, { strict: true }), | ||
); | ||
}).toThrow("batched(): incomplete batch"); | ||
}); | ||
|
||
it("throw error when n is less than 1", () => { | ||
expect(() => { | ||
take(Number.POSITIVE_INFINITY, batched([1, 1, 1, 1, 1, 1, 2, 2], 0)); | ||
}).toThrow("n must be at least one"); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* @example | ||
* ```typescript | ||
* batched('ABCDEFG', 3) // => ABC DEF G | ||
* ``` | ||
*/ | ||
export function* batched<T>( | ||
iterable: Iterable<T>, | ||
n: number, | ||
options: { strict?: boolean } = {}, | ||
): IterableIterator<T[]> { | ||
const { strict = false } = options; | ||
|
||
if (n < 1) { | ||
throw new Error("n must be at least one"); | ||
} | ||
|
||
const iterator = iterable[Symbol.iterator](); | ||
|
||
while (true) { | ||
const batch: T[] = []; | ||
for (let i = 0; i < n; i++) { | ||
const { value, done } = iterator.next(); | ||
if (done) { | ||
if (batch.length === 0) { | ||
return; | ||
} | ||
if (strict && batch.length !== n) { | ||
throw new Error("batched(): incomplete batch"); | ||
} | ||
yield batch; | ||
return; | ||
} | ||
batch.push(value); | ||
} | ||
yield batch; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { chain } from "./chain"; | ||
import { take } from "./take"; | ||
|
||
describe("concat", () => { | ||
it("concat two iterables", () => { | ||
const result = take(Number.POSITIVE_INFINITY, chain("ABC", "DEF")); | ||
|
||
expect(result).toEqual(["A", "B", "C", "D", "E", "F"]); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { flatten } from "./flatten"; | ||
|
||
/** | ||
* Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, | ||
* until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence. | ||
* | ||
* @example | ||
* chain("ABC", "DEF") // => A B C D E F | ||
*/ | ||
export function chain<T>(...iterables: Iterable<T>[]): IterableIterator<T> { | ||
return flatten(iterables); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { compress } from "./compress"; | ||
import { take } from "./take"; | ||
|
||
describe("compress", () => { | ||
it("compress on empty list", () => { | ||
expect(take(Number.POSITIVE_INFINITY, compress([], []))).toEqual([]); | ||
}); | ||
|
||
it("compress removes selected items", () => { | ||
expect( | ||
take(Number.POSITIVE_INFINITY, compress("ABCDEF", [1, 0, 1, 0, 1, 1])), | ||
).toEqual(["A", "C", "E", "F"]); | ||
|
||
expect( | ||
take( | ||
Number.POSITIVE_INFINITY, | ||
compress("ABCDEF", [true, false, true, false, true, true]), | ||
), | ||
).toEqual(["A", "C", "E", "F"]); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { zip } from "./zip"; | ||
|
||
/** | ||
* Compress an iterable by filter out data that is not selected. | ||
* @example | ||
* compress('ABCDEF', [1,0,1,0,1,1]) // => A C E F | ||
*/ | ||
export function* compress<T>( | ||
data: Iterable<T>, | ||
selectors: Iterable<number | boolean>, | ||
): IterableIterator<T> { | ||
for (const [d, selector] of zip(data, selectors)) { | ||
if (selector) { | ||
yield d; | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { flatten } from "./flatten"; | ||
import { take } from "./take"; | ||
|
||
describe("flatten", () => { | ||
it("return flatten iterables in iterables of iterables", () => { | ||
const result = take( | ||
Number.POSITIVE_INFINITY, | ||
flatten([ | ||
[1, 2], | ||
[3, 4], | ||
]), | ||
); | ||
|
||
expect(result).toEqual([1, 2, 3, 4]); | ||
}); | ||
|
||
it("only one level flatten", () => { | ||
const result = take( | ||
Number.POSITIVE_INFINITY, | ||
flatten([[[1, 2]], [[3, 4]]]), | ||
); | ||
|
||
expect(result).toEqual([ | ||
[1, 2], | ||
[3, 4], | ||
]); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Return an flatten iterator only one level of nesting in iterables of iterables | ||
* | ||
* @example | ||
* flatten([[0, 1], [2, 3]]) // => 0 1 2 3 | ||
*/ | ||
export function* flatten<T>( | ||
nestedIterables: Iterable<Iterable<T>>, | ||
): IterableIterator<T> { | ||
for (const iterable of nestedIterables) { | ||
for (const it of iterable) { | ||
yield it; | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { starmap } from "./starmap"; | ||
import { take } from "./take"; | ||
|
||
describe("starmap", () => { | ||
it("starmap with pre-zipped tuple", () => { | ||
const result = take( | ||
Number.POSITIVE_INFINITY, | ||
starmap(Math.pow, [ | ||
[2, 5], | ||
[3, 2], | ||
[10, 3], | ||
]), | ||
); | ||
|
||
expect(result).toEqual([32, 9, 1000]); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* @example | ||
* ```typescript | ||
* starmap(pow, [[2,5], [3,2], [10,3]]) // => 32 9 1000 | ||
* ``` | ||
*/ | ||
export function* starmap<T extends unknown[], R>( | ||
func: (...args: T) => R, | ||
iterable: Iterable<T>, | ||
): IterableIterator<R> { | ||
for (const args of iterable) { | ||
yield func(...args); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { take } from "./take"; | ||
import { zip } from "./zip"; | ||
|
||
describe("zip", () => { | ||
it("zips an iterable correctly", () => { | ||
const result = take(Number.POSITIVE_INFINITY, zip([1, 2], [3, 4])); | ||
expect(result).toEqual([ | ||
[1, 3], | ||
[2, 4], | ||
]); | ||
}); | ||
|
||
it("return an empty iterables when pass empty iterables", () => { | ||
const result = take(Number.POSITIVE_INFINITY, zip([], [])); | ||
expect(result).toEqual([]); | ||
}); | ||
|
||
it("throw Error pass different length iterables when strict option is true", () => { | ||
expect(() => { | ||
take(Number.POSITIVE_INFINITY, zip([1, 2], [1, 2, 3], { strict: true })); | ||
}).toThrow("zip() argument 2 is longer than argument 1"); | ||
|
||
expect(() => { | ||
take(Number.POSITIVE_INFINITY, zip([1, 2, 3], [1, 2], { strict: true })); | ||
}).toThrow("zip() argument 1 is longer than argument 2"); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/** | ||
* | ||
* @example | ||
* [...zip([1, 2], [1, 2])] // => [[1, 1], [2, 2]] | ||
* [...zip([1, 2], [1, 2, 3], { strict: true })] // => Error("zip() argument 2 is longer than argument 1") | ||
*/ | ||
export function* zip<T1, T2>( | ||
xs: Iterable<T1>, | ||
ys: Iterable<T2>, | ||
options: { strict?: boolean } = {}, | ||
): IterableIterator<[T1, T2]> { | ||
const { strict = false } = options; | ||
|
||
const [ixs, iys] = [xs[Symbol.iterator](), ys[Symbol.iterator]()]; | ||
|
||
while (true) { | ||
const [x, y] = [ixs.next(), iys.next()]; | ||
if (!x.done && !y.done) { | ||
yield [x.value, y.value]; | ||
} else { | ||
if (strict) { | ||
throw new Error( | ||
`zip() argument ${x.done ? 2 : 1} is longer than argument ${ | ||
x.done ? 1 : 2 | ||
}`, | ||
); | ||
} | ||
return; | ||
} | ||
} | ||
} |