add sample
This commit is contained in:
parent
2e2c6a8b3b
commit
85ace80d93
23
README.md
23
README.md
@ -16,7 +16,8 @@ import { matches } from '@yanick/remeda';
|
||||
|
||||
## Additional functions
|
||||
|
||||
### `matches`
|
||||
### `matches(target, matcher)`
|
||||
### `matches(matcher)(target)`
|
||||
|
||||
Compares the input with the matcher and returns `true` if they match.
|
||||
The matcher can be a function (which will be fed the input and is expected
|
||||
@ -37,3 +38,23 @@ pipe({a:1,b:2}, R.matches({ b: (val) => val < 5 }) ) // => true
|
||||
pipe({a:1,b:2}, R.matches({ c: 3 }) ) // => false
|
||||
pipe( { 'a': 4, 'b': 5, 'c': 6 }, R.matches({ 'a': 4, 'c': 6 })) // => true
|
||||
```
|
||||
|
||||
### `sample(target, size | { size: number, repeating: boolean })`
|
||||
### `sample(size | { size: number, repeating: boolean })(target)`
|
||||
|
||||
Returns random elements of the array. If `size` is bigger than the array
|
||||
length and `repeating` is false, returns a number of samples equal to
|
||||
the size of the whole array.
|
||||
|
||||
```
|
||||
sample([1,2,3,4],2); // [3,1]
|
||||
sample([1,2,3,4],{size:5, repeating: true}); // [3,1,2,2,4]
|
||||
sample([1,2,3,4],{size:5, repeating: false}); // [3,1,2,4]
|
||||
R.pipe(
|
||||
[{a: 5}, {a: 1}, {a: 3}],
|
||||
R.sumBy(x => x.a)
|
||||
) // 9
|
||||
R.sample(2)([1,2,3,4]); // [3,1]
|
||||
R.sample({size:5, repeating: true},[1,2,3,4]); // [3,1,2,2,4]
|
||||
R.sample({size:5, repeating: false},[1,2,3,4]); // [3,1,2,4]
|
||||
```
|
||||
|
43
src/sample.test.ts
Normal file
43
src/sample.test.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { describe, test, expect, vi, afterEach, beforeEach } from 'vitest';
|
||||
import { sample } from './sample';
|
||||
|
||||
const list = [1, 2, 3, 4, 5];
|
||||
|
||||
describe('data first', () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(global.Math, 'random').mockReturnValue(0.41);
|
||||
});
|
||||
afterEach(() => {
|
||||
vi.spyOn(global.Math, 'random').mockRestore();
|
||||
});
|
||||
|
||||
test('sample of 1', () => {
|
||||
expect(sample(list, 1)).toEqual([3]);
|
||||
});
|
||||
test('sample of 2', () => {
|
||||
expect(sample(list, 2)).toEqual([3, 2]);
|
||||
});
|
||||
test('sample of 20 (> than the array size)', () => {
|
||||
expect(sample(list, 20)).toEqual([3, 2, 4, 1, 5]);
|
||||
});
|
||||
describe('repeating', () => {
|
||||
test('sample of 2', () => {
|
||||
expect(sample(list, { size: 2, repeating: true })).toEqual([3, 3]);
|
||||
});
|
||||
test('sample of 20', () => {
|
||||
expect(sample(list, { size: 20, repeating: true })).toHaveLength(20);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('data last', () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(global.Math, 'random').mockReturnValue(0.41);
|
||||
});
|
||||
afterEach(() => {
|
||||
vi.spyOn(global.Math, 'random').mockRestore();
|
||||
});
|
||||
test('sample of 2', () => {
|
||||
expect(sample(2)(list)).toEqual([3, 2]);
|
||||
});
|
||||
});
|
83
src/sample.ts
Normal file
83
src/sample.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { purry } from 'remeda';
|
||||
|
||||
export interface SampleType {
|
||||
size: number;
|
||||
repeating: boolean;
|
||||
}
|
||||
|
||||
function _sample<T>(items: Array<T>, options: number | SampleType) {
|
||||
let nbrSamples = typeof options === 'number' ? options : options.size;
|
||||
const repeating = typeof options === 'object' && options?.repeating;
|
||||
|
||||
let size = items.length;
|
||||
|
||||
// we only need a copy if we're in non-repeating mode
|
||||
if (!repeating) items = [...items];
|
||||
|
||||
if (nbrSamples > size && !repeating) nbrSamples = size;
|
||||
|
||||
const sample = [];
|
||||
|
||||
for (let i = 0; i < nbrSamples; i++) {
|
||||
const index = Math.floor(Math.random() * size);
|
||||
|
||||
sample.push(items[index]);
|
||||
|
||||
if (!repeating) {
|
||||
items[index] = items[size - 1];
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random elements of the array.
|
||||
* @param items the array
|
||||
* @param options The number of samples to take. If the same item can be
|
||||
* picked multiple times, you can pass the object `{ size: number, repeating: boolean }`.
|
||||
* If the number of samples to return exceeds the array size, and `repeating`
|
||||
* is false, returns a number of sample equals to the size of the array.
|
||||
* @signature
|
||||
* R.sample(array,size)
|
||||
* R.sample(array, {size, repeating})
|
||||
* @example
|
||||
* R.sample([1,2,3,4],2); // [3,1]
|
||||
* R.sample([1,2,3,4],{size:5, repeating: true}); // [3,1,2,2,4]
|
||||
* R.sample([1,2,3,4],{size:5, repeating: false}); // [3,1,2,4]
|
||||
* R.pipe(
|
||||
* [{a: 5}, {a: 1}, {a: 3}],
|
||||
* R.sumBy(x => x.a)
|
||||
* ) // 9
|
||||
* @data_first
|
||||
* @category Array
|
||||
*/
|
||||
export function sample<T>(items: ReadonlyArray<T>, size: number): Array<T>;
|
||||
export function sample<T>(
|
||||
items: ReadonlyArray<T>,
|
||||
options: SampleType
|
||||
): Array<T>;
|
||||
|
||||
/**
|
||||
* Returns random elements of the array.
|
||||
* @param options The number of samples to take. If the same item can be
|
||||
* picked multiple times, you can pass the object `{ size: number, repeating: boolean }`.
|
||||
* If the number of samples to return exceeds the array size, and `repeating`
|
||||
* is false, returns a number of sample equals to the size of the array.
|
||||
* @signature
|
||||
* R.sample(size)(array)
|
||||
* R.sample({size, repeating})(array)
|
||||
* @example
|
||||
* R.sample(2)([1,2,3,4]); // [3,1]
|
||||
* R.sample({size:5, repeating: true},[1,2,3,4]); // [3,1,2,2,4]
|
||||
* R.sample({size:5, repeating: false},[1,2,3,4]); // [3,1,2,4]
|
||||
* @data_last
|
||||
* @category Array
|
||||
*/
|
||||
export function sample(size: number): <T>(items: Array<T>) => Array<T>;
|
||||
export function sample(options: SampleType): <T>(items: Array<T>) => Array<T>;
|
||||
|
||||
export function sample() {
|
||||
return purry(_sample, arguments);
|
||||
}
|
Loading…
Reference in New Issue
Block a user