Skip to content

Commit

Permalink
feat: add model-related functions
Browse files Browse the repository at this point in the history
- Add `randomByModel`
- Add `randomIntByModel`
- Add `randomIntInclusiveByModel`
- Add `randomByWeightModel`
- Add `randomByWeighted`

BREAKING CHANGE:
- Rename `randomByWeight` to `randomIndexByWeight`
- CommonJS => ESM
- It requires Node.js >= v18.17.0
  • Loading branch information
BlackGlory committed Apr 17, 2024
1 parent 4d726f2 commit 35777f3
Show file tree
Hide file tree
Showing 32 changed files with 1,617 additions and 3,334 deletions.
6 changes: 0 additions & 6 deletions .eslintignore

This file was deleted.

17 changes: 0 additions & 17 deletions .eslintrc.js

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x]
node-version: [18.x]

steps:
- uses: actions/checkout@v2
Expand Down
55 changes: 51 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ yarn add extra-rand
```

## API
```ts
type IRandomModel =
| number
| {
min: number
max: number
}
| NonEmptyArray<{
weight: number
value: IRandomModel
}>
```
### random
```ts
function random(min: number, max: number): number
Expand All @@ -35,13 +48,47 @@ The function returns an integer in the range `[Math.ceil(min), Math.floor(max)]`
function randomBool(probabilityOfTrue: number): boolean
```

### randomByWeight
### randomIndexByWeight
```ts
function randomByWeight(weights: number[]): number
function randomIndexByWeight(weights: NonEmptyArray<number>): number
```

The function returns an index of one of weights.

### randomWeighted
```ts
interface IWeighted {
weight: number
}
function randomWeighted<T extends IWeighted>(values: NonEmptyArray<T>): T
```

### randomByWeightModel
```ts
type IWeightModel<T> = NonEmptyArray<{
weight: number
value: T
}>
function randomByWeightModel<T>(model: IWeightModel<T>): number
```

### randomByModel
```ts
function randomByModel(model: IRandomModel): number
```

### randomIntByModel
```ts
function randomIntByModel(model: IRandomModel): number
```

### randomIntInclusiveByModel
```ts
function randomIntInclusiveByModel(model: IRandomModel): number
```

### mapToRange
```ts
function mapToRange(
Expand All @@ -55,10 +102,10 @@ A low-level function helps you to use random number generators other than `Math.

### mapToIndexByWeight
```ts
function mapByWeight(
function mapToIndexByWeight(
value: number
, oldMin: number, oldMax: number
, weights: number[]
, weights: NonEmptyArray<number>
): number
```

Expand Down
20 changes: 11 additions & 9 deletions __tests__/map-to-index-by-weight.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { mapToIndexByWeight } from '@src/map-to-index-by-weight'
import { describe, test, expect } from 'vitest'
import { mapToIndexByWeight } from '@src/map-to-index-by-weight.js'
import { NonEmptyArray } from 'justypes'

describe('mapToIndexByWeight', () => {
describe('[0, 1] to weights', () => {
test('weights: [0, 0]', () => {
const oldMin = 0
const oldMax = 1
const weights = [0, 0]
const weights: NonEmptyArray<number> = [0, 0]

expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(0)
expect(mapToIndexByWeight(0.5, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -15,7 +17,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [0, 100]', () => {
const oldMin = 0
const oldMax = 1
const weights = [0, 100]
const weights: NonEmptyArray<number> = [0, 100]

expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(1)
expect(mapToIndexByWeight(0.5, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -25,7 +27,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [100, 0]', () => {
const oldMin = 0
const oldMax = 1
const weights = [0, 100]
const weights: NonEmptyArray<number> = [0, 100]

expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(1)
expect(mapToIndexByWeight(0.5, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -35,7 +37,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [50, 50]', () => {
const oldMin = 0
const oldMax = 1
const weights = [50, 50]
const weights: NonEmptyArray<number> = [50, 50]

expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(0)
expect(mapToIndexByWeight(0.5, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -47,7 +49,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [0, 0]', () => {
const oldMin = -1
const oldMax = 1
const weights = [0, 0]
const weights: NonEmptyArray<number> = [0, 0]

expect(mapToIndexByWeight(-1, oldMin, oldMax, weights)).toBe(0)
expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -57,7 +59,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [0, 100]', () => {
const oldMin = -1
const oldMax = 1
const weights = [0, 100]
const weights: NonEmptyArray<number> = [0, 100]

expect(mapToIndexByWeight(-1, oldMin, oldMax, weights)).toBe(1)
expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(1)
Expand All @@ -67,7 +69,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [100, 0]', () => {
const oldMin = -1
const oldMax = 1
const weights = [100, 0]
const weights: NonEmptyArray<number> = [100, 0]

expect(mapToIndexByWeight(-1, oldMin, oldMax, weights)).toBe(0)
expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(0)
Expand All @@ -77,7 +79,7 @@ describe('mapToIndexByWeight', () => {
test('weights: [50, 50]', () => {
const oldMin = -1
const oldMax = 1
const weights = [50, 50]
const weights: NonEmptyArray<number> = [50, 50]

expect(mapToIndexByWeight(-1, oldMin, oldMax, weights)).toBe(0)
expect(mapToIndexByWeight(0, oldMin, oldMax, weights)).toBe(1)
Expand Down
3 changes: 2 additions & 1 deletion __tests__/map-to-range.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mapToRange } from '@src/map-to-range'
import { describe, test, expect } from 'vitest'
import { mapToRange } from '@src/map-to-range.js'

describe('mapToRange', () => {
test('[0, 1] to [10, 20]', () => {
Expand Down
3 changes: 2 additions & 1 deletion __tests__/random-bool.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { randomBool } from '@src/random-bool'
import { test, expect } from 'vitest'
import { randomBool } from '@src/random-bool.js'

test('randomBool', () => {
const loops = 10000
Expand Down
22 changes: 12 additions & 10 deletions __tests__/random-by-weight.spec.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
import { randomByWeight } from '@src/random-by-weight'
import { describe, test, expect } from 'vitest'
import { randomIndexByWeight } from '@src/random-index-by-weight.js'
import { NonEmptyArray } from 'justypes'

describe('randomByWeight', () => {
describe('randomIndexByWeight', () => {
test('[0, 100]', () => {
const weights = [0, 100]
const weights: NonEmptyArray<number> = [0, 100]

for (let i = 10000; i--;) {
const index = randomByWeight(weights)
const index = randomIndexByWeight(weights)

expect(index === 1).toBe(true)
}
})

test('[100, 0]', () => {
const weights = [100, 0]
const weights: NonEmptyArray<number> = [100, 0]

for (let i = 10000; i--;) {
const index = randomByWeight(weights)
const index = randomIndexByWeight(weights)

expect(index === 0).toBe(true)
}
})

test('[50, 50]', () => {
const loops = 10000
const weights = [50, 50]
const weights: NonEmptyArray<number> = [50, 50]

const counters = [0, 0]
for (let i = loops; i--;) {
const index = randomByWeight(weights)
const index = randomIndexByWeight(weights)
counters[index]++
}

Expand All @@ -39,11 +41,11 @@ describe('randomByWeight', () => {

test('[20, 80]', () => {
const loops = 10000
const weights = [20, 80]
const weights: NonEmptyArray<number> = [20, 80]

const counters = [0, 0]
for (let i = loops; i--;) {
const index = randomByWeight(weights)
const index = randomIndexByWeight(weights)
counters[index]++
}

Expand Down
23 changes: 11 additions & 12 deletions __tests__/random-int-inclusive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { randomIntInclusive } from '@src/random-int-inclusive'
import { test, expect } from 'vitest'
import { randomIntInclusive } from '@src/random-int-inclusive.js'

describe('randomIntInclusive(min: number, max: number): number', () => {
it('returns an integer in [Math.ceil(min), Math.floor(max)]', () => {
const min = 0.1
const max = 9.9
test('randomIntInclusive', () => {
const min = 0.1
const max = 9.9

for (let i = 10000; i--;) {
const result = randomIntInclusive(min, max)
for (let i = 10000; i--;) {
const result = randomIntInclusive(min, max)

expect(Number.isInteger(result)).toBe(true)
expect(result).toBeGreaterThanOrEqual(Math.ceil(min))
expect(result).toBeLessThanOrEqual(Math.floor(max))
}
})
expect(Number.isInteger(result)).toBe(true)
expect(result).toBeGreaterThanOrEqual(Math.ceil(min))
expect(result).toBeLessThanOrEqual(Math.floor(max))
}
})
23 changes: 11 additions & 12 deletions __tests__/random-int.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { randomInt } from '@src/random-int'
import { test, expect } from 'vitest'
import { randomInt } from '@src/random-int.js'

describe('randomInt(min: number, max: number): number', () => {
it('returns an integer in [Math.ceil(min), Math.floor(max))', () => {
const min = 0.1
const max = 9.9
test('randomInt', () => {
const min = 0.1
const max = 9.9

for (let i = 10000; i--;) {
const result = randomInt(min, max)
for (let i = 10000; i--;) {
const result = randomInt(min, max)

expect(Number.isInteger(result)).toBe(true)
expect(result).toBeGreaterThanOrEqual(Math.ceil(min))
expect(result).toBeLessThan(Math.floor(max))
}
})
expect(Number.isInteger(result)).toBe(true)
expect(result).toBeGreaterThanOrEqual(Math.ceil(min))
expect(result).toBeLessThan(Math.floor(max))
}
})
21 changes: 10 additions & 11 deletions __tests__/random.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { random } from '@src/random'
import { test, expect } from 'vitest'
import { random } from '@src/random.js'

describe('random(min: number, max: number): number', () => {
it('returns a number in [min, max)', () => {
const min = 0.1
const max = 9.9
test('random', () => {
const min = 0.1
const max = 9.9

for (let i = 10000; i--;) {
const result = random(min, max)
for (let i = 10000; i--;) {
const result = random(min, max)

expect(result).toBeGreaterThanOrEqual(min)
expect(result).toBeLessThan(max)
}
})
expect(result).toBeGreaterThanOrEqual(min)
expect(result).toBeLessThan(max)
}
})
File renamed without changes.
14 changes: 14 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @ts-check

import js from '@eslint/js'
import ts from 'typescript-eslint'

export default ts.config(
js.configs.recommended
, ...ts.configs.recommended
, {
rules: {
'require-yield': 'off'
}
}
)
10 changes: 0 additions & 10 deletions jest.config.js

This file was deleted.

Loading

0 comments on commit 35777f3

Please sign in to comment.