-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(shuffle): add string, array, and object shuffle capabaility
Also refactor and reorganize each capability and combine them also into a unified method.
- Loading branch information
Showing
24 changed files
with
1,167 additions
and
393 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
128 changes: 128 additions & 0 deletions
128
packages/individual/shuffle/src/RandomShuffle/RandomArrayShuffle/index.ts
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,128 @@ | ||
// TODO: handle typed arrays | ||
|
||
import { RandomGenerator, RandomEngine } from '@grandom/core' | ||
|
||
import { getRest } from '../utils' | ||
// import ExcludeFilter from './ExcludeFilter' | ||
|
||
export interface ShuffleArrayOptions<T = any> { | ||
/** | ||
* Filters the input array by element. | ||
* | ||
* @param element The specific element to filter from the input array. | ||
* @returns Whether to keep the specific element in the shuffle pool. | ||
*/ | ||
filter?: (element: T) => boolean | ||
|
||
/** | ||
* Exclude one or multiple elements from the input array. | ||
*/ | ||
exclude?: T | T[] | ||
} | ||
|
||
export default class RandomArrayShuffle extends RandomGenerator { | ||
constructor (engine: RandomEngine) { | ||
super(engine) | ||
|
||
this.shuffle = this.shuffle.bind(this) | ||
} | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
canBeParsed (arg1: any): boolean { | ||
return Array.isArray(arg1) | ||
} | ||
|
||
parse (arg1: any, arg2: any, arg3: any): any { | ||
if (arg1.length < 1) { | ||
return [] | ||
} | ||
|
||
const { count, options } = getRest(arg1, arg2, arg3) | ||
|
||
const filter = typeof options.filter === 'function' | ||
? options.filter as ShuffleArrayOptions['filter'] | ||
: undefined | ||
|
||
const length = count === -1 | ||
? arg1.length | ||
: count | ||
|
||
const pool: any[] = [] | ||
|
||
for (let i = 0; i < length; i++) { | ||
const element = arg1[i] | ||
|
||
if (filter?.(element) === false) { | ||
continue | ||
} | ||
|
||
pool.push(element) | ||
} | ||
|
||
this._engine.shuffleArray(pool) | ||
|
||
return pool | ||
} | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
/** | ||
* Shuffles (mixes) the input array elements randomly and returns them | ||
* as a new array (the input array is not modified). | ||
* | ||
* @param array The array to use to shuffle. | ||
*/ | ||
shuffle <T> (array: ArrayLike<T>): T | ||
|
||
/** | ||
* Shuffles (mixes) the input array elements randomly and returns them | ||
* as a new array (the input array is not modified). | ||
* | ||
* @param array The array to use to shuffle. | ||
* @param count The count (length) of the returned array. | ||
*/ | ||
shuffle <T> ( | ||
array: ArrayLike<T>, | ||
count: number | ||
): T | ||
|
||
/** | ||
* Shuffles (mixes) the input array elements randomly and returns them | ||
* as a new array (the input array is not modified). | ||
* | ||
* @param array The array to use to shuffle. | ||
* @param options Shuffle options (filtering and fallback). | ||
*/ | ||
shuffle <T> ( | ||
array: ArrayLike<T>, | ||
options: ShuffleArrayOptions<T> | ||
): T | ||
|
||
/** | ||
* Shuffles (mixes) the input array elements randomly and returns them | ||
* as a new array (the input array is not modified). | ||
* | ||
* @param array The array to use to shuffle. | ||
* @param count The count (length) of the returned array. | ||
* @param options Shuffle options (filtering and fallback). | ||
*/ | ||
shuffle <T> ( | ||
array: ArrayLike<T>, | ||
count: number, | ||
options: ShuffleArrayOptions<T> | ||
): T | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
shuffle (arg1: any, arg2?: any, arg3?: any): any { | ||
if (this.canBeParsed(arg1)) { | ||
return this.parse(arg1, arg2, arg3) | ||
} | ||
|
||
throw new TypeError( | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
`Must be called with an array, got: ${arg1} (typeof === '${typeof arg1}').` | ||
) | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
packages/individual/shuffle/src/RandomShuffle/RandomObjectShuffle/index.ts
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,129 @@ | ||
import { RandomGenerator, RandomEngine } from '@grandom/core' | ||
|
||
import { getRest } from '../utils' | ||
// import ExcludeFilter from './ExcludeFilter' | ||
|
||
export interface ShuffleObjectOptions<T = any> { | ||
/** | ||
* Filters the input object by entry. | ||
* | ||
* @param entry The specific entry to filter from the input object. | ||
* @returns Whether to keep the specific entry in the shuffle pool. | ||
*/ | ||
filter?: (key: keyof T, value: T[keyof T]) => boolean | ||
|
||
/** | ||
* Exclude one or multiple elements from the input array. | ||
*/ | ||
exclude?: T | ||
} | ||
|
||
export default class RandomObjectShuffle extends RandomGenerator { | ||
constructor (engine: RandomEngine) { | ||
super(engine) | ||
|
||
this.shuffle = this.shuffle.bind(this) | ||
} | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
canBeParsed (arg1: any): boolean { | ||
return arg1 !== null && typeof arg1 === 'object' | ||
} | ||
|
||
parse (arg1: any, arg2: any, arg3: any): any { | ||
const keys = Object.keys(arg1) | ||
|
||
if (keys.length < 1) { | ||
return {} | ||
} | ||
|
||
const { count, options } = getRest(arg1, arg2, arg3) | ||
|
||
const filter = typeof options.filter === 'function' | ||
? options.filter as ShuffleObjectOptions['filter'] | ||
: undefined | ||
|
||
const length = count === -1 | ||
? keys.length | ||
: count | ||
|
||
const pool: Record<string, any> = {} | ||
|
||
this._engine.shuffleArray(keys) | ||
|
||
for (let i = 0; i < length; i++) { | ||
const key = keys[i] | ||
const value = arg1[key] | ||
|
||
if (filter?.(key, value) === false) { | ||
continue | ||
} | ||
|
||
pool[key] = value | ||
} | ||
|
||
return pool | ||
} | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
/** | ||
* Shuffles (mixes) the input object entries randomly and returns them | ||
* as a new object (the input object is not modified). | ||
* | ||
* @param object The object to use to shuffle. | ||
*/ | ||
shuffle <T extends Record<string, any>> (object: T): T | ||
|
||
/** | ||
* Shuffles (mixes) the input object entries randomly and returns them | ||
* as a new object (the input object is not modified). | ||
* | ||
* @param object The object to use to shuffle. | ||
* @param count The count (length) of the returned object. | ||
*/ | ||
shuffle <T extends Record<string, any>> ( | ||
object: T, | ||
count: number | ||
): T | ||
|
||
/** | ||
* Shuffles (mixes) the input object entries randomly and returns them | ||
* as a new object (the input object is not modified). | ||
* | ||
* @param object The object to use to shuffle. | ||
* @param options Shuffle options (filtering and fallback). | ||
*/ | ||
shuffle <T extends Record<string, any>> ( | ||
object: T, | ||
options: ShuffleObjectOptions<T> | ||
): T | ||
|
||
/** | ||
* Shuffles (mixes) the input object entries randomly and returns them | ||
* as a new object (the input object is not modified). | ||
* | ||
* @param object The object to use to shuffle. | ||
* @param count The count (length) of the returned object. | ||
* @param options Shuffle options (filtering and fallback). | ||
*/ | ||
shuffle <T extends Record<string, any>> ( | ||
object: T, | ||
count: number, | ||
options: ShuffleObjectOptions<T> | ||
): T | ||
|
||
// ----------------------------------------------------------------------------------------------- | ||
|
||
shuffle (arg1: any, arg2?: any, arg3?: any): any { | ||
if (this.canBeParsed(arg1)) { | ||
return this.parse(arg1, arg2, arg3) | ||
} | ||
|
||
throw new TypeError( | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
`Must be called with an object, got: ${arg1} (typeof === '${typeof arg1}').` | ||
) | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
packages/individual/shuffle/src/RandomShuffle/RandomStringShuffle/ExcludeFilter/index.ts
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,58 @@ | ||
interface FilterEntry { | ||
string?: string | ||
regexp?: RegExp | ||
} | ||
|
||
export default class ExcludeFilter { | ||
private readonly _filterEntries: FilterEntry[] = [] | ||
|
||
constructor (exclude: string | RegExp | Array<string | RegExp>) { | ||
if ((typeof exclude !== 'string' && typeof exclude !== 'object') || exclude === null) { | ||
throw new TypeError( | ||
'Filter must be a string, RegExp, or Array<string | RegExp>, got: ' + | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
`${exclude} (typeof === '${typeof exclude}').` | ||
) | ||
} | ||
|
||
const filters = Array.isArray(exclude) | ||
? exclude | ||
: [exclude] | ||
|
||
let i = 0 | ||
|
||
for (const filter of filters) { | ||
if (typeof filter === 'string') { | ||
this._filterEntries.push({ string: filter }) | ||
} else if (filter instanceof RegExp) { | ||
this._filterEntries.push({ regexp: filter }) | ||
} else { | ||
throw new TypeError( | ||
`Filter[${i}] must be a string, or a RegExp, got: ` + | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
`${filter} (typeof === '${typeof filter}').` | ||
) | ||
} | ||
|
||
i++ | ||
} | ||
} | ||
|
||
get numFilters (): number { | ||
return this._filterEntries.length | ||
} | ||
|
||
matches (value: string): boolean { | ||
for (const filterEntry of this._filterEntries) { | ||
if (value === filterEntry.string) { | ||
return true | ||
} | ||
|
||
if (filterEntry.regexp?.test(value) === true) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
} |
Oops, something went wrong.