Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added a new wordList option for user-supplied word list #48

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## UNRELEASED
- Adds a new `wordList` option.

## 2.0.1 (2024-01-25)

- Fixed return type of `generate` so that it is consistent with the possibility of returning either `string` or `string[]`
Expand Down
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# random-words

## Generate one or more common English words
## Generate one or more common English words (by default)

`random-words` generates random words for use as sample text. We use it to generate random blog posts when testing [Apostrophe](http://apostrophecms.org).

Cryptographic-quality randomness is NOT the goal, as speed matters for generating sample text and security does not. As such, `Math.random()` is used in most cases.
Cryptographic-quality randomness is NOT the goal, as speed matters for generating sample text and security does not.
As such, `Math.random()` is used in most cases.

The `seed` option can be used with the `generate` function for situations that require deterministic output. When given the same `seed` with the same input, `generate()` will yield deterministic results, in regards to both actual word selection and the number of words returned (when using `min` and `max`). The underlying implementation of this option utilizes the [seedrandom](https://www.npmjs.com/package/seedrandom) package as a replacement for `Math.random()`.
The `seed` option can be used with the `generate` function for situations that require deterministic output.
When given the same `seed` with the same input, `generate()` will yield deterministic results,
in regards to both actual word selection and the number of words returned (when using `min` and `max`).
The underlying implementation of this option utilizes the [seedrandom](https://www.npmjs.com/package/seedrandom) package as a replacement for `Math.random()`.

The `count` function can be used to calculate the total number of words in the word list that meet the specified minimum and maximum length criteria.

Installation:
A default list of common English words is embedded in (and exported as `wordList` from) the package.
But both `generate` and `count` functions can be supplied a custom list of words
by passing a string array to the `wordList` option.

npm install random-words
### Installation

Examples:
```
npm install random-words
```

```js
### Examples

```javascript
import { generate, count } from "random-words";

console.log(generate());
Expand All @@ -25,6 +35,9 @@ console.log(generate());
console.log(generate(5));
//output: ['army', 'beautiful', 'became', 'if', 'actually']

console.log(generate({ wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] }));
//output: 'hello world'

console.log(generate({ minLength: 2 }));
//output: 'hello'

Expand Down Expand Up @@ -111,4 +124,9 @@ console.log(count({ maxLength: 7 }));
console.log(count({ minLength: 5, maxLength: 7 }));
//output: 1015

```
console.log(count({ wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] }));
//output: 5

console.log(count({ minLength: 8, wordList: ["hello world", "bonjour le monde", "hallo welt", "こんにちは世界", "สวัสดีชาวโลก"] }));
//output: 4
```
5 changes: 3 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ declare type GenerateOptions = {
separator?: string;
formatter?: (word: string, index: number) => string;
seed?: string;
wordList?: string[];
};

declare type JoinedWordsOptions = GenerateOptions & { join: string };
Expand All @@ -16,7 +17,7 @@ declare function generate(count?: number): string | string[];
declare function generate(options: GenerateOptions): string | string[];
declare function generate(options: JoinedWordsOptions): string;

declare const wordsList: string[];
declare const wordList: string[];

declare type CountOptions = {
minLength?: number;
Expand All @@ -25,4 +26,4 @@ declare type CountOptions = {

declare function count(options?: CountOptions): number;

export { generate, count, wordsList };
export { generate, count, wordList };
73 changes: 48 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1956,29 +1956,51 @@ export const wordList = [
"zulu",
];

const shortestWordSize = wordList.reduce((shortestWord, currentWord) =>
currentWord.length < shortestWord.length ? currentWord : shortestWord
).length;
function shortestWordSize(wordList) {
return wordList.reduce((shortestWord, currentWord) =>
currentWord.length < shortestWord.length ? currentWord : shortestWord
).length;
}

function longestWordSize(wordList) {
return wordList.reduce((longestWord, currentWord) =>
currentWord.length > longestWord.length ? currentWord : longestWord
).length;
}

const longestWordSize = wordList.reduce((longestWord, currentWord) =>
currentWord.length > longestWord.length ? currentWord : longestWord
).length;
// custom word lists are filtered and validated against emptiness;
// otherwise the default list is returned
function validateWordList(list) {
if (!Array.isArray(list) || list.length === 0) {
return wordList;
}
const cleanedList = list.filter(val => typeof val === "string")
return cleanedList.length > 0 ? cleanedList : wordList;
}

export function generate(options) {
// initalize random number generator for words if options.seed is provided
const random = options?.seed ? new seedrandom(options.seed) : null;

// if `options` is just a number, set `exactly` in options to return that many words
if (typeof options === "number") {
options = { exactly: options };
}

const { minLength, maxLength, ...rest } = options || {};

const list = validateWordList(rest?.wordList);
delete rest.wordList;

function word() {
let min =
typeof minLength !== "number"
? shortestWordSize
: limitWordSize(minLength);
typeof minLength !== "number" ?
shortestWordSize(list) :
limitWordSize(minLength);

const max =
typeof maxLength !== "number"
? longestWordSize
? longestWordSize(list)
: limitWordSize(maxLength);

if (min > max) min = max;
Expand All @@ -1993,13 +2015,13 @@ export function generate(options) {
}

function generateRandomWord() {
return wordList[randInt(wordList.length)];
return list[randInt(list.length)];
}

// limits the size of words to the minimum and maximum possible
function limitWordSize(wordSize) {
if (wordSize < shortestWordSize) wordSize = shortestWordSize;
if (wordSize > longestWordSize) wordSize = longestWordSize;
if (wordSize < shortestWordSize(list)) wordSize = shortestWordSize(list);
if (wordSize > longestWordSize(list)) wordSize = longestWordSize(list);
return wordSize;
}

Expand All @@ -2009,15 +2031,13 @@ export function generate(options) {
return Math.floor(r * lessThan);
}

// No arguments = generate one word
// No argument = Generates one word as-is
if (options === undefined) {
return word();
}

// Just a number = return that many words
if (typeof options === "number") {
options = { exactly: options };
} else if (Object.keys(rest).length === 0) {
// if `options` only contains minLength, maxLength, or wordList, generate one word as-is
if (Object.keys(rest).length === 0) {
return word();
}

Expand All @@ -2034,7 +2054,7 @@ export function generate(options) {

//not a function = returns the raw word
if (typeof options.formatter !== "function") {
options.formatter = (word) => word;
options.formatter = word => word;
}

//not a string = separator is a space
Expand All @@ -2043,7 +2063,7 @@ export function generate(options) {
}

const total = options.min + randInt(options.max + 1 - options.min);
let results = [];
const results = [];
let token = "";
let relativeIndex = 0;

Expand All @@ -2060,25 +2080,28 @@ export function generate(options) {
relativeIndex = 0;
}
}

if (typeof options.join === "string") {
results = results.join(options.join);
return results.join(options.join);
}

return results;
}

export function count(options) {
let { minLength, maxLength } = options || {};
let { minLength, maxLength, ...rest } = options || {};

const list = validateWordList(rest?.wordList);

if (typeof minLength !== "number") {
minLength = shortestWordSize;
minLength = shortestWordSize(list);
}

if (typeof maxLength !== "number") {
maxLength = longestWordSize;
maxLength = longestWordSize(list);
}

return wordList.filter(
return list.filter(
(word) => word.length >= minLength && word.length <= maxLength
).length;
}
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "random-words",
"version": "2.0.1",
"description": "Generate one or more common English words",
"version": "2.0.2",
"description": "Generate one or more common English words (by default)",
"main": "index.js",
"types": "index.d.ts",
"type": "module",
Expand All @@ -27,6 +27,6 @@
"seedrandom": "^3.0.5"
},
"devDependencies": {
"mocha": "^10.2.0"
"mocha": "^10.3.0"
}
}
}
Loading