Skip to content

Commit

Permalink
Add tee utility using single shared queue
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed May 3, 2018
1 parent 8565af9 commit c43824b
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ This function returns a list of tuples, where the `i`-th tuple contains the `i`-
zip([1, 2, 3], ['a', 'b', 'c']) //=> [[1, 'a'], [2, 'b'], [3, 'c']]
```

### `tee<T>(iterable: Iterable<T>): [Iterable<T>, Iterable<T>]`

Return two independent iterables from a single iterable.

```ts
tee([1, 2, 3]) //=> [[1, 2, 3], [1, 2, 3]]
```

### `chunk<T>(iterable: Iterable<T>, size: number): Iterable<T[]>`

Break iterable into lists of length `size`.
Expand Down
34 changes: 33 additions & 1 deletion src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,39 @@ describe('iterative', () => {
})
})

describe('chunked', () => {
describe('tee', () => {
it('should return two independent iterables from one', () => {
const iterable = iter.map([1, 2, 3], x => x * 2)
const [a, b] = iter.tee(iterable)

expect(Array.from(a)).toEqual([2, 4, 6])
expect(Array.from(b)).toEqual([2, 4, 6])
})

it('should read varying from cache to iterable', () => {
const iterable = iter.range(0, 5)
const [a, b] = iter.tee(iterable).map(iter.iter)

expect([a.next().value, a.next().value]).toEqual([0, 1])
expect([b.next().value, b.next().value, b.next().value]).toEqual([0, 1, 2])

expect([a.next().value, a.next().value]).toEqual([2, 3])
expect([b.next().value, b.next().value]).toEqual([3, 4])

expect(a.next().value).toEqual(4)
expect(b.next().value).toEqual(undefined)
})

it('should tee empty iterable', () => {
const iterable = iter.range(0, 0)
const [a, b] = iter.tee(iterable)

expect(Array.from(a)).toEqual([])
expect(Array.from(b)).toEqual([])
})
})

describe('chunk', () => {
it('should chunk an iterable', () => {
const iterable = iter.chunk([1, 2, 3, 4, 5, 6], 2)

Expand Down
35 changes: 35 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,41 @@ export function * zip <T> (...iterables: Array<Iterable<T>>): Iterable<T[]> {
}
}

/**
* Return two independent iterables from a single iterable.
*/
export function tee <T> (iterable: Iterable<T>): [Iterable<T>, Iterable<T>] {
const queue: T[] = []
const it = iter(iterable)
let owner: -1 | 0 | 1

function * gen (id: 0 | 1): Iterable<T> {
while (true) {
while (queue.length) {
yield queue.shift()!
}

if (owner === -1) return

let item: IteratorResult<T>

while (item = it.next()) {
if (item.done) {
owner = -1
return
}

owner = id
queue.push(item.value)
yield item.value
if (id !== owner) break
}
}
}

return [gen(0), gen(1)]
}

/**
* Break iterable into lists of length `size`.
*/
Expand Down

0 comments on commit c43824b

Please sign in to comment.