Skip to content

Commit

Permalink
add array/kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
mo-ba committed Oct 21, 2023
1 parent 37c81bc commit 9d53db1
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 147 deletions.
66 changes: 66 additions & 0 deletions docs/src/pages/en/array/operator/kernel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: kernel
description: kernel
layout: ../../../../layouts/MainLayout.astro
---
Splits an array into subarrays (kernels) of a specified `windowSize`,
moving the window by `stride` elements at a time.
## Type

```ts
type kernel = (stride: number) => (windowSize: number) => <T>(array: T[]) => T[][]
```
## Parameters
- `stride` (number): The step size to move the window in the input array. Must be greater than or equal to 1.
- `windowSize` (number): The size of each window.
- `array` (Array): An array of elements to split into windows.
## Returns
- (Array of Arrays): An array of subarrays (windows) containing
elements from the input array.
## Example
```ts
const inputArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const stride = 2;
const windowSize = 3;

const resultWindows = kernel(stride)(windowSize)(inputArray);
// -> [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]
```

## Notes
- The `stride` parameter determines the step size to move the window
in the input `array`.
- It should be a positive integer greater than or equal to 1.
- If the `stride` is less than 1, an error will be thrown.

- The `windowSize` parameter determines the size of each window.
It should be a positive integer (greater than or equal to 1).
- If the `windowSize` is less than 1, an error will be thrown.

- The function splits the input `array` into subarrays (windows)
of the specified size, moving the window `stride` elements at a time.
If the input array is too short to create a window of the specified
size, an empty array is included in the result.

- The input `array` remains unaltered, and a new array of subarrays
is returned.

- If the `windowSize` is greater than the length of the input array,
the result will be an empty array.

- If the input `array` is empty, an empty array is returned.



## See Also

- [windowed](./windowed)
- [stride](./stride)
- [pairwise](./pairwise)
- [chunkBySize](./chunkBySize)
2 changes: 2 additions & 0 deletions docs/src/pages/en/array/operator/windowed.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ the result will be an empty array.

## See Also

- [kernel](./kernel)
- [stride](./stride)
- [pairwise](./pairwise)
6 changes: 3 additions & 3 deletions spec/core/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ describe('array', () => {
'exists', 'fill', 'filter',
'find', 'findBack', 'findIndex',
'findIndexBack', 'flatten', 'fold',
'foldBack', 'forall', 'groupBy',
'head', 'init', 'insertAt', 'isEmpty',
'iter', 'last', 'length',
'foldBack', 'forall', 'groupBy', 'head',
'init', 'insertAt', 'interleave', 'isEmpty',
'iter', 'kernel', 'last', 'length',
'map', 'maxBy', 'minBy',
'pairwise', 'partition', 'push',
'reduce', 'reduceBack', 'replicate',
Expand Down
62 changes: 27 additions & 35 deletions spec/core/array.timing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ const oneSecond = 1000;
const oneMinute = 60 * oneSecond;

const print = (!process.env.PRINT_DEBUG)
? () => null
: (data: unknown) => console.log(data);
? () => null
: (data: unknown) => console.log(data);


function printTimings(a: number[], b: number[]) {
Expand Down Expand Up @@ -77,12 +77,16 @@ function run<T, R>(fnxt: (array: T[]) => R, alt: (array: T[]) => R, data: T[], r

describe('performance test', function () {

beforeEach((done) => {
setTimeout(done, 50);
});

const length = 100000;

it('partition', () => {
const partition = <T>(predicate: Predicate<T>) =>
(array: T[]): Tuple<T[], T[]> =>
[array.filter(predicate), array.filter(e => !predicate(e))];
(array: T[]): Tuple<T[], T[]> =>
[array.filter(predicate), array.filter(e => !predicate(e))];

const data = shuffle(ARRAY.range(0, length));
const predicate: Predicate<number> = (e) => length / 2 <= e;
Expand Down Expand Up @@ -120,7 +124,7 @@ describe('performance test', function () {
describe('remove', () => {
it('remove (1)', () => {
const remove = (index: number) => <T>(array: T[]): T[] =>
array.filter((v, i) => i !== index);
array.filter((v, i) => i !== index);

const data = ARRAY.range(0, length);

Expand All @@ -137,11 +141,11 @@ describe('performance test', function () {
it('collect (1)', () => {

const collect = <E, F>(fn: UnaryFunction<E, Iterable<F>>) => (array: E[]): F[] =>
array.reduce((p, c) => {
const values = fn(c);
p.push(...values);
return p;
}, [] as F[]);
array.reduce((p, c) => {
const values = fn(c);
p.push(...values);
return p;
}, [] as F[]);


const data = ARRAY.range(0, length / 10);
Expand Down Expand Up @@ -184,7 +188,7 @@ describe('performance test', function () {
describe('updateAt', () => {
it('updateAt (1)', () => {
const updateAt = (index: number) => <T>(value: T) => (array: T[]): T[] =>
array.map((v, i) => i === index ? value : v);
array.map((v, i) => i === index ? value : v);

const data = ARRAY.range(0, length);

Expand All @@ -198,7 +202,7 @@ describe('performance test', function () {

it('updateAt (2)', () => {
const updateAt = (index: number) => <T>(value: T) => (array: T[]): T[] =>
array.slice(0, index).concat([value], array.slice(index + 1));
array.slice(0, index).concat([value], array.slice(index + 1));

const data = ARRAY.range(0, length);

Expand Down Expand Up @@ -343,34 +347,22 @@ describe('performance test', function () {
}).timeout(oneMinute);

it('rotate', () => {
const rotate = (offset: number) => <S>(array: S[]): S[] => {
const [front, back] = ARRAY.splitAt(offset %= array.length)(array);
return ARRAY.append(back)(front);
};

const data = ARRAY.range(0, length);
const half = Math.round(length / 2);

run(ARRAY.rotate(half), rotate(half), data);
}).timeout(oneMinute);

it('rotate 2', () => {
const rotate = (offset: number) =>

<S>(array: S[]): S[] => {
<S>(array: S[]): S[] => {

const index = offset % array.length + (offset < 0 ? array.length : 0);
const result: S[] = [];
for (let i = index; i < array.length; i++) {
result.push(array[i]);
}
const index = offset % array.length + (offset < 0 ? array.length : 0);
const result: S[] = [];
for (let i = index; i < array.length; i++) {
result.push(array[i]);
}

for (let i = 0; i < index; i++) {
result.push(array[i]);
}
for (let i = 0; i < index; i++) {
result.push(array[i]);
}

return result;
};
return result;
};

const data = ARRAY.range(0, length);
const half = Math.round(length / 2);
Expand Down
63 changes: 63 additions & 0 deletions spec/core/array/operator/kernel.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {expect} from 'chai';

import {checkThrow} from '../../../support/checkThrow';
import {chunkBySize, kernel, range, windowed} from '../../../../src/array';

describe('kernel', () => {

it('should kernel 1:2', () => {
const array = [1, 2, 3, 4];
const fn = kernel(2)(2);
expect(fn(array)).to.eql([[1, 2], [3, 4]]);
expect(fn([1, 2])).to.eql([[1, 2]]);
expect(fn([1])).to.eql([]);
});
it('should kernel 2:3', () => {
const array = [1, 2, 3, 4, 5, 6, 7];
const fn = kernel(2)(3);
expect(fn(array)).to.eql([[1, 2, 3], [3, 4, 5], [5, 6, 7]]);
expect(fn([1, 2, 3])).to.eql([[1, 2, 3]]);
expect(fn([1, 2])).to.eql([]);
expect(fn([1])).to.eql([]);
});
describe('iterate', () => {
for (let i = 1; i < 50; i += Math.ceil(Math.random() * 5)) {

it(`should kernel 1:${i} equals windowed ${i}`, () => {
const array = range(0, 100);
const fn = kernel(1)(i);
expect(fn(array)).to.eql(windowed(i)(array));
});
it(`should kernel ${i}:${i} equals chunkBySize ${i} if array length is multiple of ${i}`, () => {
const array = range(0, Math.floor(100 / i) * i);
const fn = kernel(i)(i);
expect(fn(array)).to.eql(chunkBySize(i)(array));
});
}
});

it('should return empty if window greater than length', () => {
const fn = kernel(1)(2);
expect(fn([1])).to.eql([]);
});

it('should throw if windowSize is less than 1', () => {
expect(() => kernel(1)(0)).to.throw(`windowSize must not be less than 1. (0 given)`);
expect(() => kernel(1)(-1)).to.throw(`windowSize must not be less than 1. (-1 given)`);
});
it('should throw if stride is less than 1', () => {
expect(() => kernel(0)(1)).to.throw(`stride must not be less than 1. (0 given)`);
expect(() => kernel(-1)(1)).to.throw(`stride must not be less than 1. (-1 given)`);
});


it('should return empty', () => {
const fn = kernel(1)(2);
expect(fn([])).to.eql([]);
});

it('should throw if null or undefined', () => {
const fn = kernel(1)(2);
checkThrow(fn);
});
});
Loading

0 comments on commit 9d53db1

Please sign in to comment.