aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2022-10-09 21:26:25 +0200
committerGitHub <[email protected]>2022-10-09 21:26:25 +0200
commita6ce71703b02f7c2c4f742106acff05d879c4384 (patch)
tree0eb6baa6789d1b87a836c49b14b7869e5a3df673 /src/modules
parent5dc8f0e82e38215feab362b1a748065c890f570d (diff)
downloadfaker-a6ce71703b02f7c2c4f742106acff05d879c4384.tar.xz
faker-a6ce71703b02f7c2c4f742106acff05d879c4384.zip
feat: lorem null response fix (#1407)
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/lorem/index.ts42
-rw-r--r--src/modules/word/filterWordListByLength.ts98
-rw-r--r--src/modules/word/index.ts222
3 files changed, 305 insertions, 57 deletions
diff --git a/src/modules/lorem/index.ts b/src/modules/lorem/index.ts
index 4745b0e9..ac1ba756 100644
--- a/src/modules/lorem/index.ts
+++ b/src/modules/lorem/index.ts
@@ -1,4 +1,5 @@
import type { Faker } from '../..';
+import { filterWordListByLength } from '../word/filterWordListByLength';
/**
* Module to generate random texts and words.
@@ -17,24 +18,43 @@ export class LoremModule {
/**
* Generates a word of a specified length.
*
- * @param length length of the word that should be returned. Defaults to a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.lorem.word() // 'temporibus'
* faker.lorem.word(5) // 'velit'
+ * faker.lorem.word({ strategy: 'shortest' }) // 'a'
+ * faker.lorem.word({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'quaerat'
*
* @since 3.1.0
*/
- word(length?: number): string {
- const hasRightLength = (word: string) => word.length === length;
- let properLengthWords: readonly string[];
- if (length == null) {
- properLengthWords = this.faker.definitions.lorem.words;
- } else {
- properLengthWords =
- this.faker.definitions.lorem.words.filter(hasRightLength);
- }
- return this.faker.helpers.arrayElement(properLengthWords);
+ word(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
+ return this.faker.helpers.arrayElement(
+ filterWordListByLength({
+ ...opts,
+ wordList: this.faker.definitions.lorem.words,
+ })
+ );
}
/**
diff --git a/src/modules/word/filterWordListByLength.ts b/src/modules/word/filterWordListByLength.ts
new file mode 100644
index 00000000..ca312371
--- /dev/null
+++ b/src/modules/word/filterWordListByLength.ts
@@ -0,0 +1,98 @@
+import { FakerError } from '../../errors/faker-error';
+
+/**
+ * The error handling strategies for the `filterWordListByLength` function.
+ *
+ * Always returns a new array.
+ */
+const STRATEGIES = {
+ fail: () => {
+ throw new FakerError('No words found that match the given length.');
+ },
+ closest: (
+ wordList: string[],
+ length: { min: number; max: number }
+ ): string[] => {
+ const wordsByLength = wordList.reduce((data, word) => {
+ (data[word.length] = data[word.length] ?? []).push(word);
+ return data;
+ }, {} as Record<number, string[]>);
+
+ const lengths = Object.keys(wordsByLength).map(Number);
+ const min = Math.min(...lengths);
+ const max = Math.max(...lengths);
+
+ const closestOffset = Math.min(length.min - min, max - length.max);
+
+ return wordList.filter(
+ (word) =>
+ word.length === length.min - closestOffset ||
+ word.length === length.max + closestOffset
+ );
+ },
+ shortest: (wordList: string[]): string[] => {
+ const minLength = Math.min(...wordList.map((word) => word.length));
+ return wordList.filter((word) => word.length === minLength);
+ },
+ longest: (wordList: string[]): string[] => {
+ const maxLength = Math.max(...wordList.map((word) => word.length));
+ return wordList.filter((word) => word.length === maxLength);
+ },
+ 'any-length': (wordList: string[]): string[] => {
+ return [...wordList];
+ },
+} as const; /*
+satisfies Record<
+string, // Parameters<filterWordListByLength>[0]['strategy']
+(wordList: string[], length: { min: number; max: number }) => string[]
+>;
+*/
+
+/**
+ * Filters a string array for values with a matching length.
+ * If length is not provided or no values with a matching length are found,
+ * then the result will be determined using the given error handling strategy.
+ *
+ * @param options The options to provide.
+ * @param options.wordList A list of words to filter.
+ * @param options.length The exact or the range of lengths the words should have.
+ * @param options.strategy The strategy to apply when no words with a matching length are found. Defaults to 'any-length'.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a copy of the original word list.
+ */
+export function filterWordListByLength(options: {
+ wordList: string[];
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+}): string[] {
+ const { wordList, length, strategy = 'any-length' } = options;
+
+ if (length) {
+ const filter: (word: string) => boolean =
+ typeof length === 'number'
+ ? (word) => word.length === length
+ : (word) => word.length >= length.min && word.length <= length.max;
+
+ const wordListWithLengthFilter = wordList.filter(filter);
+
+ if (wordListWithLengthFilter.length > 0) {
+ return wordListWithLengthFilter;
+ }
+
+ if (typeof length === 'number') {
+ return STRATEGIES[strategy](wordList, { min: length, max: length });
+ } else {
+ return STRATEGIES[strategy](wordList, length);
+ }
+ } else if (strategy === 'shortest' || strategy === 'longest') {
+ return STRATEGIES[strategy](wordList);
+ } else {
+ return [...wordList];
+ }
+}
diff --git a/src/modules/word/index.ts b/src/modules/word/index.ts
index 725c0c42..9bdf8f06 100644
--- a/src/modules/word/index.ts
+++ b/src/modules/word/index.ts
@@ -1,29 +1,5 @@
import type { Faker } from '../..';
-
-/**
- * Filters a string array for values with a specific length.
- * If length is not provided or no values with this length there found a copy of the original array is returned.
- *
- * @param options The options to provide
- * @param options.wordList A list of word to filter
- * @param options.length The exact length words should have
- */
-function filterWordListByLength(options: {
- wordList: string[];
- length?: number;
-}): string[] {
- if (!options.length) {
- return options.wordList;
- }
-
- const wordListWithLengthFilter = options.wordList.filter(
- (word) => word.length === options.length
- );
-
- return wordListWithLengthFilter.length > 0
- ? wordListWithLengthFilter
- : [...options.wordList];
-}
+import { filterWordListByLength } from './filterWordListByLength';
/**
* Module to return various types of words.
@@ -42,20 +18,42 @@ export class WordModule {
/**
* Returns an adjective of random or optionally specified length.
*
- * @param length Expected adjective length. If specified length is unresolvable, returns adjective of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.adjective() // 'pungent'
* faker.word.adjective(5) // 'slimy'
* faker.word.adjective(100) // 'complete'
+ * faker.word.adjective({ strategy: 'shortest' }) // 'icy'
+ * faker.word.adjective({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'distant'
*
* @since 6.0.0
*/
- adjective(length?: number): string {
+ adjective(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.adjective,
- length,
})
);
}
@@ -63,20 +61,42 @@ export class WordModule {
/**
* Returns an adverb of random or optionally specified length.
*
- * @param length Expected adverb length. If specified length is unresolvable, returns adverb of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.adverb() // 'quarrelsomely'
* faker.word.adverb(5) // 'madly'
* faker.word.adverb(100) // 'sadly'
+ * faker.word.adverb({ strategy: 'shortest' }) // 'too'
+ * faker.word.adverb({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'sweetly'
*
* @since 6.0.0
*/
- adverb(length?: number): string {
+ adverb(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.adverb,
- length,
})
);
}
@@ -84,20 +104,42 @@ export class WordModule {
/**
* Returns a conjunction of random or optionally specified length.
*
- * @param length Expected conjunction length. If specified length is unresolvable, returns conjunction of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.conjunction() // 'in order that'
* faker.word.conjunction(5) // 'since'
* faker.word.conjunction(100) // 'as long as'
+ * faker.word.conjunction({ strategy: 'shortest' }) // 'or'
+ * faker.word.conjunction({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'hence'
*
* @since 6.0.0
*/
- conjunction(length?: number): string {
+ conjunction(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.conjunction,
- length,
})
);
}
@@ -105,20 +147,42 @@ export class WordModule {
/**
* Returns an interjection of random or optionally specified length.
*
- * @param length Expected interjection length. If specified length is unresolvable, returns interjection of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.interjection() // 'gah'
* faker.word.interjection(5) // 'fooey'
* faker.word.interjection(100) // 'yowza'
+ * faker.word.interjection({ strategy: 'shortest' }) // 'hm'
+ * faker.word.interjection({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'boohoo'
*
* @since 6.0.0
*/
- interjection(length?: number): string {
+ interjection(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.interjection,
- length,
})
);
}
@@ -126,20 +190,42 @@ export class WordModule {
/**
* Returns a noun of random or optionally specified length.
*
- * @param length Expected noun length. If specified length is unresolvable, returns noun of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.noun() // 'external'
* faker.word.noun(5) // 'front'
* faker.word.noun(100) // 'care'
+ * faker.word.noun({ strategy: 'shortest' }) // 'ad'
+ * faker.word.noun({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'average'
*
* @since 6.0.0
*/
- noun(length?: number): string {
+ noun(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.noun,
- length,
})
);
}
@@ -147,20 +233,42 @@ export class WordModule {
/**
* Returns a preposition of random or optionally specified length.
*
- * @param length Expected preposition length. If specified length is unresolvable, returns preposition of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.preposition() // 'without'
* faker.word.preposition(5) // 'abaft'
* faker.word.preposition(100) // 'an'
+ * faker.word.preposition({ strategy: 'shortest' }) // 'a'
+ * faker.word.preposition({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'given'
*
* @since 6.0.0
*/
- preposition(length?: number): string {
+ preposition(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.preposition,
- length,
})
);
}
@@ -168,20 +276,42 @@ export class WordModule {
/**
* Returns a verb of random or optionally specified length.
*
- * @param length Expected verb length. If specified length is unresolvable, returns verb of a random length.
+ * @param options The expected length of the word or the options to use.
+ * @param options.length The expected length of the word.
+ * @param options.strategy The strategy to apply when no words with a matching length are found.
+ *
+ * Available error handling strategies:
+ *
+ * - `fail`: Throws an error if no words with the given length are found.
+ * - `shortest`: Returns any of the shortest words.
+ * - `closest`: Returns any of the words closest to the given length.
+ * - `longest`: Returns any of the longest words.
+ * - `any-length`: Returns a word with any length.
+ *
+ * Defaults to `'any-length'`.
*
* @example
* faker.word.verb() // 'act'
* faker.word.verb(5) // 'tinge'
* faker.word.verb(100) // 'mess'
+ * faker.word.verb({ strategy: 'shortest' }) // 'do'
+ * faker.word.verb({ length: { min: 5, max: 7 }, strategy: "fail" }) // 'vault'
*
* @since 6.0.0
*/
- verb(length?: number): string {
+ verb(
+ options:
+ | number
+ | {
+ length?: number | { min: number; max: number };
+ strategy?: 'fail' | 'closest' | 'shortest' | 'longest' | 'any-length';
+ } = {}
+ ): string {
+ const opts = typeof options === 'number' ? { length: options } : options;
return this.faker.helpers.arrayElement(
filterWordListByLength({
+ ...opts,
wordList: this.faker.definitions.word.verb,
- length,
})
);
}