Skip to content

Commit

Permalink
Non uniform distro and other functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ogroppo committed Nov 20, 2023
1 parent bbd6785 commit 432106f
Show file tree
Hide file tree
Showing 19 changed files with 165 additions and 52 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# deverything

## 0.36.0

### Minor Changes

- non-uniform distro

## 0.35.0

### Minor Changes
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ Contributions always welcome!
- `getKeys()` get the keys of an object, includes symbols
- `getUrlSearchParam()`
- `getUrlSearchParams()`
- `incrementalId()` autoincremental SQL-like, process-unique numeric id
- `keysLength()`
- `last()` get the last element of an array
- `lastIndex()` get the last index of an array
-`merge()` deep merge objects
- `moveToFirst()` move array element to first
- `moveToIndex()` move array element to desired index
Expand Down Expand Up @@ -101,7 +104,7 @@ These functions are optimized for low entropy random data generation useful for

- `randomAddress()`
- `randomAlphaNumericCode()`
-`randomArrayItem()`
-`randomArrayItem()` now supporting non-uniform distribution
- `randomBankAccount()`
- `randomBool()`
- `randomChar()`
Expand Down Expand Up @@ -136,9 +139,9 @@ These functions are optimized for low entropy random data generation useful for
- `randomLastName()`
- `randomFullName()`
- `randomNumericCode()`
- `randomNumericId()` autoincremental process-unique id
- `randomParagraph()`
- `randomPassword()`
- `randomPath()` /path/to/something
- `randomPhoneNumber()`
- `randomString()`
- `randomUUID()` lightweight uuid generation, passing UUID validation
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deverything",
"version": "0.35.0",
"version": "0.36.0",
"description": "Everything you need for Dev",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
Expand Down
13 changes: 7 additions & 6 deletions src/constants/addresses.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type Address = {
// Prefix with Random to avoid name collision with the Address type from other libs
export type RandomAddress = {
city: string;
country: string;
countryCode: string;
Expand All @@ -9,7 +10,7 @@ export type Address = {
zip: string;
};

export const UK_ADDRESSES: Address[] = [
export const UK_ADDRESSES: RandomAddress[] = [
{
city: "London",
country: "United Kingdom",
Expand All @@ -29,7 +30,7 @@ export const UK_ADDRESSES: Address[] = [
},
];

export const US_ADDRESSES: Address[] = [
export const US_ADDRESSES: RandomAddress[] = [
{
city: "New York",
country: "United States",
Expand All @@ -51,7 +52,7 @@ export const US_ADDRESSES: Address[] = [
},
];

export const EUROPE_ADDRESSES: Address[] = [
export const EUROPE_ADDRESSES: RandomAddress[] = [
{
city: "Paris",
country: "France",
Expand Down Expand Up @@ -88,7 +89,7 @@ export const EUROPE_ADDRESSES: Address[] = [
},
];

export const MIXED_ADDRESSES: Address[] = [
export const MIXED_ADDRESSES: RandomAddress[] = [
{
city: "Moscow",
country: "Russia",
Expand Down Expand Up @@ -165,7 +166,7 @@ export const MIXED_ADDRESSES: Address[] = [
},
];

export const ADDRESSES: Address[] = [
export const ADDRESSES: RandomAddress[] = [
...UK_ADDRESSES,
...US_ADDRESSES,
...EUROPE_ADDRESSES,
Expand Down
71 changes: 42 additions & 29 deletions src/constants/words.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,48 @@
export const VERBS = [
"abide",
"abound",
"accept",
"accomplish",
"achieve",
"acquire",
"act",
"adapt",
"add",
"adjust",
"admire",
"admit",
"adopt",
"advance",
"advise",
"afford",
"agree",
"alert",
"allow",
"be",
"catch",
"create",
"delete",
"discover",
"eat",
"explore",
"go",
"help",
"imagine",
"jump",
"merge",
"need",
"offer",
"play",
"read",
"run",
"search",
"skip",
"solve",
"speak",
"think",
"try",
"work",
"write",
];

export const NOUNS = [
"city",
"coffee",
"courage",
"fact",
"family",
"flower",
"food",
"friend",
"fun",
"hope",
"justice",
"key",
"life",
"love",
"music",
Expand All @@ -48,29 +59,31 @@ export const ADJECTIVES = [
"fascinating",
"interesting",
"playful",
"predictable",
"remarkable",
"soothing",
"sunny",
"unforgettable",
"wonderful",
"predictable",
];

// TODO: curate
export const ADVERBS = [
"abnormally",
"aboard",
"absentmindedly",
"absolutely",
"absurdly",
"abundantly",
"abysmally",
"academically",
"acceleratedly",
"accentually",
"acceptably",
"accessibly",
"accidentally",
"accommodatingly",
"boldly",
"briskly",
"carefully",
"efficiently",
"freely",
"gently",
"happily",
"lightly",
"loudly",
"quickly",
"quietly",
"suddenly",
"unexpectedly",
"wisely",
];

export const LONG_WORDS = [
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/incrementalId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let id = 1; // don't start with 0, to be closer to SQL autoincrement

export const incrementalId = () => {
return id++;
};
3 changes: 3 additions & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export * from "./firstValue";
export * from "./getKeys";
export * from "./getUrlSearchParam";
export * from "./getUrlSearchParams";
export * from "./incrementalId";
export * from "./keysLength";
export * from "./last";
export * from "./lastIndex";
export * from "./merge";
export * from "./moveToFirst";
export * from "./moveToLast";
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/keysLength.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PlainObject } from "../types";

export const keysLength = <T extends PlainObject>(obj: T) => {
return Object.keys(obj).length;
};
5 changes: 4 additions & 1 deletion src/helpers/last.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export const last = <T>(arr?: T[]): T | undefined => arr?.[arr.length - 1];
import { lastIndex } from "./lastIndex";

// Assume the array is not empty, otherwise the result needs to be banged all the time
export const last = <T>(arr: T[]): T => arr[lastIndex(arr)];
3 changes: 3 additions & 0 deletions src/helpers/lastIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const lastIndex = (array: any[]): number => {
return array.length - 1;
};
1 change: 1 addition & 0 deletions src/random/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export * from "./randomNumericCode";
export * from "./randomNumericId";
export * from "./randomParagraph";
export * from "./randomPassword";
export * from "./randomPath";
export * from "./randomPhoneNumber";
export * from "./randomString";
export * from "./randomUUID";
Expand Down
27 changes: 27 additions & 0 deletions src/random/randomArrayItem.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect } from "@jest/globals";
import { randomArrayItem } from "./randomArrayItem";
import { array } from "../helpers";

describe(`randomArrayItem`, () => {
it(`returns no item`, () => {
Expand All @@ -9,4 +10,30 @@ describe(`randomArrayItem`, () => {
expect(randomArrayItem([0])).toBe(0);
expect(typeof randomArrayItem([0, 1])).toBe("number");
});
it(`with weights non 1 sum`, () => {
const stats: { [key: number]: number } = {
0: 0,
1: 0,
};

array(100000).forEach(() => {
const random = randomArrayItem([0, 1], { weights: [22, 23] });
stats[random]++;
});
expect(stats[0]).toBeLessThan(stats[1]);
});
it(`with weights 1 sum`, () => {
const stats: { [key: number]: number } = {
0: 0,
1: 0,
2: 0,
};

array(100000).forEach(() => {
const random = randomArrayItem([0, 1, 2], { weights: [0.1, 0.2, 0.7] });
stats[random]++;
});
expect(stats[0]).toBeLessThan(stats[1]);
expect(stats[1]).toBeLessThan(stats[2]);
});
});
25 changes: 23 additions & 2 deletions src/random/randomArrayItem.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
import { lastIndex } from "../helpers/lastIndex";
import { last } from "../helpers/last";
import { randomInt } from "./randomInt";
import { sum } from "../math/sum";

export const randomArrayItem = <T>(array: T[]): T => {
return array[randomInt(0, array.length - 1)];
export const randomArrayItem = <T>(
array: T[],
{ weights }: { weights?: number[] } = {}
): T => {
if (weights && weights.length === array.length) {
const totalWeight = sum(weights);
let random = Math.random() * totalWeight;

for (let i = 0; i < weights.length; i++) {
random -= weights[i];
if (random <= 0) {
return array[i];
}
}

// In case of rounding errors, return the last item
return last(array);
}

return array[randomInt(0, lastIndex(array))];
};
9 changes: 0 additions & 9 deletions src/random/randomNumericId.test.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/random/randomNumericId.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
let id = 1; // don't start with 0, to be closer to SQL autoincrement

/**
* @deprecated use incrementalId() instead, as this one is not random and could cause confusion
*/
export const randomNumericId = () => {
return id++;
};
8 changes: 8 additions & 0 deletions src/random/randomPath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { describe, it, expect } from "@jest/globals";
import { randomPath } from "./randomPath";

describe(`randomPath`, () => {
it(`works`, () => {
expect(randomPath().includes("/")).toBe(true);
});
});
10 changes: 10 additions & 0 deletions src/random/randomPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { array } from "../helpers";
import { randomWord } from "./randomWord";

export const randomPath = ({
depth = 3,
}: {
depth?: number;
} = {}) => {
return array(depth, randomWord).join("/");
};
10 changes: 9 additions & 1 deletion src/random/randomWord.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { WORDS } from "../constants/words";
import { NOUNS, VERBS, WORDS } from "../constants/words";
import { randomArrayItem } from "./randomArrayItem";

export const randomWord = () => {
return randomArrayItem(WORDS);
};

export const randomNoun = () => {
return randomArrayItem(NOUNS);
};

export const randomVerb = () => {
return randomArrayItem(VERBS);
};
4 changes: 3 additions & 1 deletion src/validators/isLastIndex.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { lastIndex } from "../helpers/lastIndex";

export const isLastIndex = (index: number, array: any[]): boolean => {
return index === array.length - 1;
return index === lastIndex(array);
};

0 comments on commit 432106f

Please sign in to comment.