Skip to content

Commit 973160e

Browse files
committed
feat: adding a shuffle function to randomly sort arrays
1 parent 121f843 commit 973160e

File tree

4 files changed

+126
-65
lines changed

4 files changed

+126
-65
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ yarn add randm
2626
- `randm.int.between(2, 10)` - returns a random integer between 2 and 10
2727
- `randm.oneIn(10)` - one in 10 chance that this returns true, otherwise it returns false
2828
- `randm.from(['foo', 'bar', 'qux'])` - returns a random value from an array, in this case 'foo', 'bar' or 'qux'
29+
- `randm.shuffle([1, 2, 3, 4])` - returns a randomly sorted copy of the passed array e.g. [3, 2, 4, 1]. (Immutable - the passed array will not be changed)
2930
- `randm.happens(2).outOf(5)` - returns true 2 out of 5 times
3031
- `randm.percentageChance(95)` - returns true 95% of the time
3132
- `randm.coinFlip()` - returns `heads` or `tails`

randm.d.ts

Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -38,74 +38,34 @@ declare module "randm" {
3838
happens(times: number): { outOf(total: number): boolean };
3939
percentageChance(percentage: number): boolean;
4040
customDiceRoll(die: string): number;
41-
int: {
42-
between(x: number, y: number): number;
43-
};
44-
date: {
45-
between(x: Date, y: Date): Date;
46-
};
41+
int: { between(x: number, y: number): number };
42+
date: { between(x: Date, y: Date): Date };
4743
bag(contents?: any[]): Bag;
44+
shuffle<T>(arry: T[]): T[];
4845
next: {
49-
int: {
50-
between: {
51-
returns(...vals: number[]): void;
52-
};
53-
};
54-
any: {
55-
returns(...vals: number[]): void;
56-
};
57-
bool: {
58-
returns(...vals: boolean[]): void;
59-
};
60-
coinFlip: {
61-
returns(...vals: ("heads" | "tails")[]): void;
62-
};
63-
dateTime: {
64-
returns(...vals: Date[]): void;
65-
};
66-
diceRoll: {
67-
returns(...vals: number[]): void;
68-
};
69-
diceRollBeats: {
70-
returns(...vals: boolean[]): void;
71-
};
72-
diceRollOf: {
73-
returns(...vals: DiceRollDetails[]): void;
74-
};
75-
artilleryDie: {
76-
returns(...vals: (number | "MISS")[]): void;
77-
};
46+
int: { between: { returns(...vals: number[]): void } };
47+
any: { returns(...vals: number[]): void };
48+
bool: { returns(...vals: boolean[]): void };
49+
coinFlip: { returns(...vals: ("heads" | "tails")[]): void };
50+
dateTime: { returns(...vals: Date[]): void };
51+
diceRoll: { returns(...vals: number[]): void };
52+
diceRollBeats: { returns(...vals: boolean[]): void };
53+
diceRollOf: { returns(...vals: DiceRollDetails[]): void };
54+
artilleryDie: { returns(...vals: (number | "MISS")[]): void };
7855
scatterDie: {
7956
returns(...vals: { direction: number; isHit: boolean }[]): void;
8057
};
81-
between: {
82-
returns(...vals: number[]): void;
83-
};
84-
oneIn: {
85-
returns(...vals: boolean[]): void;
86-
};
87-
from: {
88-
returns<T>(...vals: T[]): void;
89-
};
90-
happens: {
91-
returns(...vals: boolean[]): void;
92-
};
93-
percentageChance: {
94-
returns(...vals: boolean[]): void;
95-
};
96-
customDiceRoll: {
97-
returns(...vals: number[]): void;
98-
};
58+
between: { returns(...vals: number[]): void };
59+
oneIn: { returns(...vals: boolean[]): void };
60+
from: { returns<T>(...vals: T[]): void };
61+
shuffle: { returns<T>(...vals: T[]): void };
62+
happens: { returns(...vals: boolean[]): void };
63+
percentageChance: { returns(...vals: boolean[]): void };
64+
customDiceRoll: { returns(...vals: number[]): void };
9965
bag: {
100-
pick: {
101-
returns(...vals: any[]): void;
102-
};
103-
contents: {
104-
returns(...vals: ReadonlyArray<any>[]): void;
105-
};
106-
isEmpty: {
107-
returns(...vals: boolean[]): void;
108-
};
66+
pick: { returns(...vals: any[]): void };
67+
contents: { returns(...vals: ReadonlyArray<any>[]): void };
68+
isEmpty: { returns(...vals: boolean[]): void };
10969
};
11070
reset(): void;
11171
};

randm.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,22 @@ const randm = {
8080
randm.happens(Math.abs(percentage)).outOf(100),
8181
customDiceRoll: (die) => randm.diceRollOf(die).rolls().total,
8282
bag: (contents) => new Bag(contents),
83+
shuffle: (arry) => {
84+
let cpy;
85+
try {
86+
cpy = [...arry];
87+
} catch (e) {
88+
return arry;
89+
}
90+
for (let i = cpy.length - 1; i > 0; i--) {
91+
const j = randm.int.between(0, i);
92+
[cpy[i], cpy[j]] = [cpy[j], cpy[i]];
93+
}
94+
return cpy;
95+
},
8396
};
8497

85-
randm.int = {
86-
between: (x, y) => Math.round(randm.any() * (y - x)) + x,
87-
};
98+
randm.int = { between: (x, y) => Math.round(randm.any() * (y - x)) + x };
8899

89100
randm.date = {
90101
between: (x, y) => new Date(randm.int.between(x.getTime(), y.getTime())),

test/shuffle.test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
var randm = require("../randm");
2+
var t = require("tap");
3+
4+
t.beforeEach(() => {
5+
randm.next.reset();
6+
});
7+
8+
t.test("randm.shuffle randomly re-orders an array of numbers", function (t) {
9+
const orig = [1, 2, 3, 4];
10+
const shuffled = randm.shuffle(orig);
11+
console.log("randm.shuffle([1, 2, 3, 4])", shuffled);
12+
t.notSame(orig, shuffled);
13+
t.end();
14+
});
15+
16+
t.test("randm.shuffle randomly re-orders an array of strings", function (t) {
17+
const orig = ["a", "b", "c", "d"];
18+
const shuffled = randm.shuffle(orig);
19+
console.log('randm.shuffle(["a", "b", "c", "d"])', shuffled);
20+
t.notSame(orig, shuffled);
21+
t.end();
22+
});
23+
24+
t.test("randm.shuffle randomly re-orders an array of objects", function (t) {
25+
const orig = [
26+
{ a: 1, b: "so" },
27+
{ a: 2, b: "huh" },
28+
{ a: 3, b: "wo" },
29+
];
30+
const shuffled = randm.shuffle(orig);
31+
console.log(
32+
'randm.shuffle([{ a: 1, b: "so" }, { a: 2, b: "huh" }, { a: 3, b: "wo" }])',
33+
shuffled
34+
);
35+
t.notSame(orig, shuffled);
36+
t.end();
37+
});
38+
39+
t.test("randm.shuffle randomly re-orders a string", function (t) {
40+
const orig = "Hello";
41+
const shuffled = randm.shuffle(orig);
42+
console.log("randm.shuffle('Hello')", shuffled);
43+
t.notSame(orig, shuffled);
44+
t.end();
45+
});
46+
47+
t.test(
48+
"randm.shuffle returns the original number argument when not passed an iterable",
49+
function (t) {
50+
const orig = 1234;
51+
const shuffled = randm.shuffle(orig);
52+
console.log("randm.shuffle(1234)", shuffled);
53+
t.same(orig, shuffled);
54+
t.equal(shuffled, 1234);
55+
t.end();
56+
}
57+
);
58+
59+
t.test("randm.shuffle does not mutate the passed array", function (t) {
60+
const orig = [1, 2, 3, 4];
61+
const shuffled = randm.shuffle(orig);
62+
t.notSame(orig, shuffled);
63+
t.same(orig, [1, 2, 3, 4]);
64+
t.end();
65+
});
66+
67+
t.test(
68+
"should be able to mock the array returned from randm.shuffle",
69+
function (t) {
70+
const originalArray = ["original", "items"];
71+
randm.next.shuffle.returns(["mocked", "items"]);
72+
const shuffled = randm.shuffle(originalArray);
73+
t.same(shuffled, ["mocked", "items"]);
74+
t.end();
75+
}
76+
);
77+
78+
t.test(
79+
"should be able to mock the subsequent calls to randm.shuffle",
80+
function (t) {
81+
const originalArray = ["original", "items"];
82+
randm.next.shuffle.returns(["mocked", "items1"], ["mocked", "items2"]);
83+
const shuffled1 = randm.shuffle(originalArray);
84+
const shuffled2 = randm.shuffle(originalArray);
85+
t.same(shuffled1, ["mocked", "items1"]);
86+
t.same(shuffled2, ["mocked", "items2"]);
87+
t.end();
88+
}
89+
);

0 commit comments

Comments
 (0)