markov-strings
Advanced tools
Comparing version 2.1.0 to 3.0.0-beta.0
export declare type MarkovGenerateOptions = { | ||
maxTries?: number; | ||
prng?: any; | ||
prng?: () => number; | ||
filter?: (result: MarkovResult) => boolean; | ||
@@ -5,0 +5,0 @@ }; |
{ | ||
"name": "markov-strings", | ||
"version": "2.1.0", | ||
"version": "3.0.0-beta.0", | ||
"description": "A Markov string generator", | ||
@@ -8,2 +8,3 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"start": "tsc --watch", | ||
"build": "tsc", | ||
@@ -31,13 +32,13 @@ "test": "jest --coverage --coverageReporters=lcov --coverageReporters=html", | ||
"devDependencies": { | ||
"@types/jest": "^24.0.15", | ||
"@types/lodash": "^4.14.136", | ||
"@types/node": "^12.6.2", | ||
"coveralls": "^3.0.5", | ||
"jest": "^24.8.0", | ||
"ts-jest": "^24.0.2", | ||
"typescript": "^3.5.3" | ||
"@types/jest": "^25.1.3", | ||
"@types/lodash": "^4.14.149", | ||
"@types/node": "^13.7.7", | ||
"coveralls": "^3.0.9", | ||
"jest": "^25.1.0", | ||
"ts-jest": "^25.2.1", | ||
"typescript": "^3.8.3" | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.14" | ||
"lodash": "^4.17.15" | ||
} | ||
} |
@@ -7,3 +7,3 @@ [![Build Status](https://travis-ci.org/scambier/markov-strings.svg?branch=master)](https://travis-ci.org/scambier/markov-strings) | ||
A simplistic Markov chain text generator. | ||
A simplistic Markov chain text generator. | ||
Give it an array of strings, and it will output a randomly generated string. | ||
@@ -13,12 +13,12 @@ | ||
- [Markov-strings](#Markov-strings) | ||
- [Prerequisites](#Prerequisites) | ||
- [Installing](#Installing) | ||
- [Usage](#Usage) | ||
- [API](#API) | ||
- [new Markov(data, [options])](#new-Markovdata-options) | ||
- [.buildCorpus()](#buildCorpus) | ||
- [Markov-strings](#markov-strings) | ||
- [Prerequisites](#prerequisites) | ||
- [Installing](#installing) | ||
- [Usage](#usage) | ||
- [API](#api) | ||
- [new Markov([options])](#new-markovoptions) | ||
- [.addData(data)](#adddatadata) | ||
- [.generate([options])](#generateoptions) | ||
- [Changelog](#Changelog) | ||
- [Running the tests](#Running-the-tests) | ||
- [Changelog](#changelog) | ||
- [Running the tests](#running-the-tests) | ||
@@ -43,5 +43,7 @@ ## Prerequisites | ||
// Build the Markov generator | ||
const markov = new Markov(data, { stateSize: 2 }) | ||
markov.buildCorpus() | ||
const markov = new Markov({ stateSize: 2 }) | ||
// Add data for the generator | ||
markov.addData(data) | ||
const options = { | ||
@@ -72,6 +74,22 @@ maxTries: 20, // Give up if I don't have a sentence after 20 tries (default is 10) | ||
### new Markov(data, [options]) | ||
### new Markov([options]) | ||
Create a generator instance. | ||
#### options | ||
```js | ||
{ | ||
stateSize: number | ||
} | ||
``` | ||
The `stateSize` is the number of words for each "link" of the generated sentence. `1` will output gibberish sentences without much sense. `2` is a sensible default for most cases. `3` and more can create good sentences if you have a corpus that allows it. | ||
### .addData(data) | ||
To function correctly, the Markov generator needs its internal data to be correctly structured. `.addData(data)` allows you add raw data, that is automatically formatted to fit the internal structure. | ||
You can call `.addData(data)` as often as you need, with new data each time. | ||
#### data | ||
@@ -87,5 +105,7 @@ | ||
`[ 'lorem ipsum', 'dolor sit amet' ]` | ||
```js | ||
[ 'lorem ipsum', 'dolor sit amet' ] | ||
``` | ||
or | ||
or | ||
@@ -99,19 +119,4 @@ ```js | ||
#### options | ||
Since `.addData(data)` can take some time (it loops for each word of each string), a non-blocking variant `.addDataAsync(data)` is conveniently available if you need it. | ||
```js | ||
{ | ||
stateSize: number | ||
} | ||
``` | ||
The `stateSize` is the number of words for each "link" of the generated sentence. `1` will output gibberish sentences without much sense. `2` is a sensible default for most cases. `3` and more can create good sentences if you have a corpus that allows it. | ||
### .buildCorpus() | ||
This function **must** be called to build the corpus for Markov generation. | ||
It will iterate over all words from your `data` parameter to create an internal optimized structure. | ||
Since `.buildCorpus()` can take some time (it loops for each word of each string), a non-blocking variant `.buildCorpusAsync()` is conveniently available if you need it. | ||
### .generate([options]) | ||
@@ -146,2 +151,6 @@ | ||
#### 3.0.0 | ||
Refactoring to facilitate iterative construction of the corpus (multiple `.addData()` instead of a one-time `buildCorpus()`) | ||
#### 2.1.0 | ||
@@ -148,0 +157,0 @@ |
import { assignIn, cloneDeep, flatten, includes, isEmpty, isString, slice, some, uniqBy } from 'lodash' | ||
function sampleWithPRNG (array: Array<any>, prng: any = Math.random) { | ||
function sampleWithPRNG<T>(array: T[], prng: () => number = Math.random): T | undefined { | ||
const length = array == null ? 0 : array.length | ||
@@ -8,5 +8,7 @@ return length ? array[Math.floor(prng() * length)] : undefined | ||
export type MarkovInputData = Array<{ string: string }> | ||
export type MarkovGenerateOptions = { | ||
maxTries?: number, | ||
prng?: any, | ||
prng?: () => number, | ||
filter?: (result: MarkovResult) => boolean | ||
@@ -22,3 +24,3 @@ } | ||
score: number, | ||
refs: Array<{ string: string }>, | ||
refs: MarkovInputData, | ||
tries: number | ||
@@ -29,3 +31,3 @@ } | ||
words: string | ||
refs: Array<{ string: string }> | ||
refs: MarkovInputData | ||
} | ||
@@ -36,3 +38,3 @@ | ||
export default class Markov { | ||
public data: Array<{ string: string }> | ||
public data: MarkovInputData | ||
public startWords: MarkovFragment[] = [] | ||
@@ -50,15 +52,7 @@ public endWords: MarkovFragment[] = [] | ||
* | ||
* @param {(string[] | Array<{ string: string }>)} data An array of strings or objects. | ||
* If 'data' is an array of objects, each object must have a 'string' attribute | ||
* @param {MarkovConstructorOptions} [options={}] | ||
* @memberof Markov | ||
*/ | ||
constructor(data: string[] | Array<{ string: string }>, options: MarkovConstructorOptions = {}) { | ||
// Format data if necessary | ||
if (isString(data[0])) { | ||
data = (data as string[]).map(s => ({ string: s })) | ||
} else if (!data[0].hasOwnProperty('string')) { | ||
throw new Error('Objects in your corpus must have a "string" property') | ||
} | ||
this.data = data as Array<{ string: string }> | ||
constructor(options: MarkovConstructorOptions = {}) { | ||
this.data = [] | ||
@@ -70,2 +64,17 @@ // Save options | ||
public addData(rawData: MarkovInputData | string[]) { | ||
// Format data if necessary | ||
let input: MarkovInputData = [] | ||
if (isString(rawData[0])) { | ||
input = (rawData as string[]).map(s => ({ string: s })) | ||
} | ||
else if (!rawData[0].hasOwnProperty('string')) { | ||
throw new Error('Objects in your corpus must have a "string" property') | ||
} | ||
this.buildCorpus(input) | ||
this.data = this.data.concat(input) | ||
} | ||
/** | ||
@@ -76,6 +85,6 @@ * Builds the corpus. You must call this before generating sentences. | ||
*/ | ||
public buildCorpus(): void { | ||
private buildCorpus(data: MarkovInputData): void { | ||
const options = this.options | ||
this.data.forEach(item => { | ||
data.forEach(item => { | ||
const line = item.string | ||
@@ -137,6 +146,6 @@ const words = line.split(' ') | ||
*/ | ||
public buildCorpusAsync(): Promise<void> { | ||
public addDataAsync(data: MarkovInputData | string[]): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
this.buildCorpus() | ||
this.addData(data) | ||
resolve() | ||
@@ -143,0 +152,0 @@ } catch (e) { |
@@ -17,11 +17,5 @@ import { map, some } from 'lodash' | ||
describe('Constructor', () => { | ||
it('should throw an error if corpus is invalid', () => { | ||
expect(() => { | ||
// @ts-ignore | ||
const markov = new Markov([{}]) | ||
}).toThrowError() | ||
}) | ||
it('should have a default stateSize', () => { | ||
const markov = new Markov(data) | ||
const markov = new Markov() | ||
expect(markov.options.stateSize).toBe(2) | ||
@@ -31,3 +25,3 @@ }) | ||
it('should save a different stateSize', () => { | ||
const markov = new Markov(data, { stateSize: 3 }) | ||
const markov = new Markov({ stateSize: 3 }) | ||
expect(markov.options.stateSize).toBe(3) | ||
@@ -37,7 +31,7 @@ }) | ||
describe('Build the corpus', () => { | ||
describe('Adding data', () => { | ||
it('should build synchronously', () => { | ||
const markov = new Markov(data) | ||
const markov = new Markov() | ||
expect(markov.corpus).toEqual({}) | ||
markov.buildCorpus() | ||
markov.addData(data) | ||
expect(markov.corpus).not.toEqual({}) | ||
@@ -47,14 +41,23 @@ }) | ||
it('should build asynchronously', async () => { | ||
const markov = new Markov(data) | ||
markov.buildCorpus = jest.fn() | ||
await markov.buildCorpusAsync() | ||
expect(markov.buildCorpus).toHaveBeenCalled() | ||
const markov = new Markov() | ||
markov.addData = jest.fn() | ||
await markov.addDataAsync(data) | ||
expect(markov.addData).toHaveBeenCalled() | ||
}) | ||
it('should throw an error if the data structure is invalid', () => { | ||
const markov = new Markov() | ||
expect(() => { | ||
// @ts-ignore | ||
markov.addData([{}]) | ||
}).toThrowError() | ||
}) | ||
}) | ||
describe('After building the corpus', () => { | ||
describe('After adding data', () => { | ||
let markov: Markov | ||
beforeEach(() => { | ||
markov = new Markov(data) | ||
markov.buildCorpus() | ||
markov = new Markov() | ||
markov.addData(data) | ||
}) | ||
@@ -94,28 +97,22 @@ | ||
}) | ||
}) | ||
describe('The corpus itself', () => { | ||
let markov: Markov | ||
beforeEach(() => { | ||
markov = new Markov(data) | ||
markov.buildCorpus() | ||
describe('The corpus itself', () => { | ||
it('should have the right values for the right keys', () => { | ||
const corpus = markov.corpus | ||
expect(some(corpus['Lorem ipsum'], { words: 'dolor sit' })).toBeTruthy() | ||
expect( | ||
some(corpus['Lorem ipsum'], { words: 'duplicate start' }) | ||
).toBeTruthy() | ||
expect( | ||
some(corpus['tempor, erat'], { words: 'vel lacinia' }) | ||
).toBeTruthy() | ||
}) | ||
}) | ||
it('should have the right values for the right keys', () => { | ||
const corpus = markov.corpus | ||
expect(some(corpus['Lorem ipsum'], { words: 'dolor sit' })).toBeTruthy() | ||
expect( | ||
some(corpus['Lorem ipsum'], { words: 'duplicate start' }) | ||
).toBeTruthy() | ||
expect( | ||
some(corpus['tempor, erat'], { words: 'vel lacinia' }) | ||
).toBeTruthy() | ||
}) | ||
}) | ||
describe('The sentence generation', () => { | ||
describe('The sentence generator', () => { | ||
let markov: Markov | ||
beforeEach(() => { | ||
markov = new Markov(data) | ||
markov.buildCorpus() | ||
markov = new Markov() | ||
markov.addData(data) | ||
}) | ||
@@ -130,3 +127,3 @@ | ||
it('should throw an error if the corpus is not built', () => { | ||
markov = new Markov(data) | ||
markov = new Markov() | ||
expect(() => { | ||
@@ -133,0 +130,0 @@ markov.generate() |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
36168
707
199
2
Updatedlodash@^4.17.15