Skip to content

Commit a5de229

Browse files
Shinigami92ST-DDT
andauthored
refactor(helpers)!: rewrite shuffle (#1521)
Co-authored-by: ST-DDT <ST-DDT@gmx.de>
1 parent 7e00d17 commit a5de229

3 files changed

Lines changed: 196 additions & 27 deletions

File tree

src/modules/helpers/index.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,29 +240,61 @@ export class HelpersModule {
240240
/**
241241
* Takes an array and randomizes it in place then returns it.
242242
*
243-
* Uses the modern version of the Fisher–Yates algorithm.
243+
* @template T The type of the entries to shuffle.
244+
* @param list The array to shuffle.
245+
* @param options The options to use when shuffling.
246+
* @param options.inplace Whether to shuffle the array in place or return a new array. Defaults to `false`.
247+
*
248+
* @example
249+
* faker.helpers.shuffle(['a', 'b', 'c'], { inplace: true }) // [ 'b', 'c', 'a' ]
250+
*
251+
* @since 8.0.0
252+
*/
253+
shuffle<T>(list: T[], options: { inplace: true }): T[];
254+
/**
255+
* Returns a randomized version of the array.
244256
*
245257
* @template T The type of the entries to shuffle.
246-
* @param o The array to shuffle. Defaults to `[]`.
258+
* @param list The array to shuffle.
259+
* @param options The options to use when shuffling.
260+
* @param options.inplace Whether to shuffle the array in place or return a new array. Defaults to `false`.
247261
*
248262
* @example
249-
* faker.helpers.shuffle() // []
250263
* faker.helpers.shuffle(['a', 'b', 'c']) // [ 'b', 'c', 'a' ]
264+
* faker.helpers.shuffle(['a', 'b', 'c'], { inplace: false }) // [ 'b', 'c', 'a' ]
251265
*
252266
* @since 2.0.1
253267
*/
254-
shuffle<T>(o?: T[]): T[] {
255-
if (o == null || o.length === 0) {
256-
return o || [];
268+
shuffle<T>(list: readonly T[], options?: { inplace?: false }): T[];
269+
/**
270+
* Returns a randomized version of the array.
271+
*
272+
* @template T The type of the entries to shuffle.
273+
* @param list The array to shuffle.
274+
* @param options The options to use when shuffling.
275+
* @param options.inplace Whether to shuffle the array in place or return a new array. Defaults to `false`.
276+
*
277+
* @example
278+
* faker.helpers.shuffle(['a', 'b', 'c']) // [ 'b', 'c', 'a' ]
279+
* faker.helpers.shuffle(['a', 'b', 'c'], { inplace: true }) // [ 'b', 'c', 'a' ]
280+
* faker.helpers.shuffle(['a', 'b', 'c'], { inplace: false }) // [ 'b', 'c', 'a' ]
281+
*
282+
* @since 2.0.1
283+
*/
284+
shuffle<T>(list: T[], options?: { inplace?: boolean }): T[];
285+
shuffle<T>(list: T[], options: { inplace?: boolean } = {}): T[] {
286+
const { inplace = false } = options;
287+
288+
if (!inplace) {
289+
list = [...list];
257290
}
258291

259-
for (let i = o.length - 1; i > 0; --i) {
292+
for (let i = list.length - 1; i > 0; --i) {
260293
const j = this.faker.datatype.number(i);
261-
const x = o[i];
262-
o[i] = o[j];
263-
o[j] = x;
294+
[list[i], list[j]] = [list[j], list[i]];
264295
}
265-
return o;
296+
297+
return list;
266298
}
267299

268300
/**

test/__snapshots__/helpers.spec.ts.snap

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ exports[`helpers > 42 > replaceSymbols > only symbols 1`] = `"3U17U5"`;
6969

7070
exports[`helpers > 42 > replaceSymbols > some string 1`] = `"^1234567890ß´°!\\"§$%&/()=J\`+71,..-;:_"`;
7171

72-
exports[`helpers > 42 > shuffle > noArgs 1`] = `[]`;
73-
7472
exports[`helpers > 42 > shuffle > with array 1`] = `
7573
[
7674
"!",
@@ -88,6 +86,40 @@ exports[`helpers > 42 > shuffle > with array 1`] = `
8886
]
8987
`;
9088

89+
exports[`helpers > 42 > shuffle > with array and inplace false 1`] = `
90+
[
91+
"!",
92+
"W",
93+
"d",
94+
"H",
95+
"l",
96+
"l",
97+
"o",
98+
" ",
99+
"e",
100+
"l",
101+
"r",
102+
"o",
103+
]
104+
`;
105+
106+
exports[`helpers > 42 > shuffle > with array and inplace true 1`] = `
107+
[
108+
"!",
109+
"W",
110+
"d",
111+
"H",
112+
"l",
113+
"l",
114+
"o",
115+
" ",
116+
"e",
117+
"l",
118+
"r",
119+
"o",
120+
]
121+
`;
122+
91123
exports[`helpers > 42 > slugify > noArgs 1`] = `""`;
92124

93125
exports[`helpers > 42 > slugify > some string 1`] = `"hello-world"`;
@@ -185,8 +217,6 @@ exports[`helpers > 1211 > replaceSymbols > only symbols 1`] = `"9L72D0"`;
185217

186218
exports[`helpers > 1211 > replaceSymbols > some string 1`] = `"^1234567890ß´°!\\"§$%&/()=Y\`+47,..-;:_"`;
187219

188-
exports[`helpers > 1211 > shuffle > noArgs 1`] = `[]`;
189-
190220
exports[`helpers > 1211 > shuffle > with array 1`] = `
191221
[
192222
"l",
@@ -204,6 +234,40 @@ exports[`helpers > 1211 > shuffle > with array 1`] = `
204234
]
205235
`;
206236

237+
exports[`helpers > 1211 > shuffle > with array and inplace false 1`] = `
238+
[
239+
"l",
240+
"l",
241+
"o",
242+
"l",
243+
"W",
244+
"d",
245+
"H",
246+
"e",
247+
"o",
248+
"r",
249+
" ",
250+
"!",
251+
]
252+
`;
253+
254+
exports[`helpers > 1211 > shuffle > with array and inplace true 1`] = `
255+
[
256+
"l",
257+
"l",
258+
"o",
259+
"l",
260+
"W",
261+
"d",
262+
"H",
263+
"e",
264+
"o",
265+
"r",
266+
" ",
267+
"!",
268+
]
269+
`;
270+
207271
exports[`helpers > 1211 > slugify > noArgs 1`] = `""`;
208272

209273
exports[`helpers > 1211 > slugify > some string 1`] = `"hello-world"`;
@@ -291,8 +355,6 @@ exports[`helpers > 1337 > replaceSymbols > only symbols 1`] = `"2OF2OA"`;
291355

292356
exports[`helpers > 1337 > replaceSymbols > some string 1`] = `"^1234567890ß´°!\\"§$%&/()=G\`+5F,..-;:_"`;
293357

294-
exports[`helpers > 1337 > shuffle > noArgs 1`] = `[]`;
295-
296358
exports[`helpers > 1337 > shuffle > with array 1`] = `
297359
[
298360
" ",
@@ -310,6 +372,40 @@ exports[`helpers > 1337 > shuffle > with array 1`] = `
310372
]
311373
`;
312374

375+
exports[`helpers > 1337 > shuffle > with array and inplace false 1`] = `
376+
[
377+
" ",
378+
"d",
379+
"o",
380+
"r",
381+
"H",
382+
"o",
383+
"!",
384+
"l",
385+
"l",
386+
"e",
387+
"W",
388+
"l",
389+
]
390+
`;
391+
392+
exports[`helpers > 1337 > shuffle > with array and inplace true 1`] = `
393+
[
394+
" ",
395+
"d",
396+
"o",
397+
"r",
398+
"H",
399+
"o",
400+
"!",
401+
"l",
402+
"l",
403+
"e",
404+
"W",
405+
"l",
406+
]
407+
`;
408+
313409
exports[`helpers > 1337 > slugify > noArgs 1`] = `""`;
314410

315411
exports[`helpers > 1337 > slugify > some string 1`] = `"hello-world"`;

test/helpers.spec.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ describe('helpers', () => {
6666
});
6767

6868
t.describe('shuffle', (t) => {
69-
t.it('noArgs').it('with array', 'Hello World!'.split(''));
69+
t.it('with array', 'Hello World!'.split(''))
70+
.it('with array and inplace true', 'Hello World!'.split(''), {
71+
inplace: true,
72+
})
73+
.it('with array and inplace false', 'Hello World!'.split(''), {
74+
inplace: false,
75+
});
7076
});
7177

7278
t.describe('uniqueArray', (t) => {
@@ -306,26 +312,61 @@ describe('helpers', () => {
306312

307313
it('mutates the input array in place', () => {
308314
const input = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
309-
const shuffled = faker.helpers.shuffle(input);
315+
const shuffled = faker.helpers.shuffle(input, { inplace: true });
310316
expect(shuffled).deep.eq(input);
311317
});
312318

313-
it('all items shuffled as expected when seeded', () => {
314-
const input = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
315-
faker.seed(100);
316-
const shuffled = faker.helpers.shuffle(input);
317-
expect(shuffled).deep.eq([
319+
it('does not mutate the input array by default', () => {
320+
const input = Object.freeze([
321+
'a',
318322
'b',
323+
'c',
324+
'd',
319325
'e',
326+
'f',
327+
'g',
328+
'h',
329+
'i',
330+
'j',
331+
]);
332+
expect(() => faker.helpers.shuffle(input)).not.to.throw();
333+
});
334+
335+
it('does not mutate the input array when inplace is false', () => {
336+
const input = Object.freeze([
320337
'a',
338+
'b',
339+
'c',
321340
'd',
322-
'j',
323-
'i',
341+
'e',
342+
'f',
343+
'g',
324344
'h',
345+
'i',
346+
'j',
347+
]);
348+
expect(() =>
349+
faker.helpers.shuffle(input, { inplace: false })
350+
).not.to.throw();
351+
});
352+
353+
it('throws an error when the input array is readonly and inplace is true', () => {
354+
const input = Object.freeze([
355+
'a',
356+
'b',
325357
'c',
326-
'g',
358+
'd',
359+
'e',
327360
'f',
361+
'g',
362+
'h',
363+
'i',
364+
'j',
328365
]);
366+
expect(() =>
367+
// @ts-expect-error: we want to test that it throws
368+
faker.helpers.shuffle(input, { inplace: true })
369+
).to.throw();
329370
});
330371
});
331372

0 commit comments

Comments
 (0)