Skip to content

Commit

Permalink
feat: add windows (#836)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranet authored Nov 22, 2024
1 parent 44fccf0 commit 4844417
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
10 changes: 10 additions & 0 deletions packages/iterator-utilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,16 @@
"default": "./dist/cjs/lib/unzip.cjs"
}
},
"./windows": {
"import": {
"types": "./dist/esm/lib/windows.d.mts",
"default": "./dist/esm/lib/windows.mjs"
},
"require": {
"types": "./dist/cjs/lib/windows.d.cts",
"default": "./dist/cjs/lib/windows.cjs"
}
},
"./zip": {
"import": {
"types": "./dist/esm/lib/zip.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/iterator-utilities/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ export * from './lib/toIterableIterator';
export * from './lib/union';
export * from './lib/unique';
export * from './lib/unzip';
export * from './lib/windows';
export * from './lib/zip';
39 changes: 39 additions & 0 deletions packages/iterator-utilities/src/lib/windows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { from, type IterableResolvable } from './from';
import { assertPositive } from './shared/_assertPositive';
import { makeIterableIterator } from './shared/_makeIterableIterator';
import { toIntegerOrInfinityOrThrow } from './shared/_toIntegerOrInfinityOrThrow';

/**
* Creates an iterable with arrays of `count` elements representing a sliding window.
*
* @param iterable The iterator to take values from.
* @param count The maximum number of values in the window.
* @returns An iterator that yields windows with `count` values from the provided iterator.
*
* @example
* ```typescript
* import { windows } from '@sapphire/iterator-utilities';
*
* const iterable = [1, 2, 3, 4, 5];
* console.log([...windows(iterable, 2)]);
* // Output: [[1, 2], [2, 3], [3, 4], [4, 5]]
* ```
*/
export function windows<const ElementType>(iterable: IterableResolvable<ElementType>, count: number): IterableIterator<ElementType[]> {
count = assertPositive(toIntegerOrInfinityOrThrow(count), count);

const buffer = [] as ElementType[];
const resolvedIterable = from(iterable);
return makeIterableIterator<ElementType[]>(() => {
while (buffer.length !== count) {
const result = resolvedIterable.next();
if (result.done) return { done: true, value: undefined };

buffer.push(result.value);
}

const value = buffer.slice();
buffer.shift();
return { done: false, value };
});
}
46 changes: 46 additions & 0 deletions packages/iterator-utilities/tests/windows.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { windows } from '../src';

describe('windows', () => {
test('GIVEN iterable and limit greater than iterable length THEN returns an empty iterable', () => {
const iterable = [1, 2, 3];
const limit = 5;
const result = [...windows(iterable, limit)];
expect(result).toEqual([]);
});

test('GIVEN iterable and limit equal to iterable length THEN returns an array with the contents of the iterable', () => {
const iterable = [1, 2, 3];
const limit = 3;
const result = [...windows(iterable, limit)];
expect(result).toEqual([[1, 2, 3]]);
});

test('GIVEN iterable and limit less than iterable length THEN returns multiple windows', () => {
const iterable = [1, 2, 3];
const limit = 2;
const result = [...windows(iterable, limit)];
expect(result).toEqual([
[1, 2],
[2, 3]
]);
});

test('GIVEN empty iterable THEN returns empty iterable', () => {
const iterable: number[] = [];
const limit = 5;
const result = [...windows(iterable, limit)];
expect(result).toEqual([]);
});

test('GIVEN iterable and limit equal to 0 THEN throws RangeError', () => {
const iterable = [1, 2, 3];
const limit = 0;
expect(() => windows(iterable, limit)).toThrowError(new RangeError('0 must be a positive number'));
});

test('GIVEN iterable and limit less than 0 THEN throws RangeError', () => {
const iterable = [1, 2, 3];
const limit = -1;
expect(() => windows(iterable, limit)).toThrowError(new RangeError('-1 must be a positive number'));
});
});

0 comments on commit 4844417

Please sign in to comment.