Skip to content

Commit

Permalink
feat: lorem null response fix (#1407)
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT authored Oct 9, 2022
1 parent 5dc8f0e commit a6ce717
Show file tree
Hide file tree
Showing 7 changed files with 550 additions and 59 deletions.
42 changes: 31 additions & 11 deletions src/modules/lorem/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Faker } from '../..';
import { filterWordListByLength } from '../word/filterWordListByLength';

/**
* Module to generate random texts and words.
Expand All @@ -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,
})
);
}

/**
Expand Down
98 changes: 98 additions & 0 deletions src/modules/word/filterWordListByLength.ts
Original file line number Diff line number Diff line change
@@ -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];
}
}
Loading

0 comments on commit a6ce717

Please sign in to comment.