From 27b17106bc78e339ff987f86837b237b09303d93 Mon Sep 17 00:00:00 2001 From: Euan MacKenzie <82283260+eumkz@users.noreply.github.com> Date: Fri, 5 Jul 2024 00:40:19 -0400 Subject: [PATCH] perf(shuffle): use the Fisher-Yates algorithm (#76) --- src/random/shuffle.ts | 19 ++++++++++++++----- tests/random/shuffle.test.ts | 10 ++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/random/shuffle.ts b/src/random/shuffle.ts index 8d4faeed..de385598 100644 --- a/src/random/shuffle.ts +++ b/src/random/shuffle.ts @@ -1,6 +1,15 @@ -export function shuffle(array: readonly T[]): T[] { - return array - .map(a => ({ rand: Math.random(), value: a })) - .sort((a, b) => a.rand - b.rand) - .map(a => a.value) +import * as _ from 'radashi' + +export function shuffle( + array: readonly T[], + random: (min: number, max: number) => number = _.random, +): T[] { + const newArray = array.slice() + for (let idx = 0, randomIdx: number, item: T; idx < array.length; idx++) { + randomIdx = random(0, array.length - 1) + item = newArray[randomIdx] + newArray[randomIdx] = newArray[idx] + newArray[idx] = item + } + return newArray } diff --git a/tests/random/shuffle.test.ts b/tests/random/shuffle.test.ts index 7d10d32c..c89f00e1 100644 --- a/tests/random/shuffle.test.ts +++ b/tests/random/shuffle.test.ts @@ -19,4 +19,14 @@ describe('shuffle', () => { expect(list).not.toBe(result) expect(list).toEqual([1, 2, 3, 4, 5]) }) + test('uses custom random function when provided', () => { + const list = [1, 2, 3, 4, 5] + const mockRandom = vi.fn(() => 1) + const result = _.shuffle(list, mockRandom) + + expect(mockRandom).toHaveBeenCalled() + expect(result).not.toEqual(list) + expect(result.length).toBe(list.length) + expect(new Set(result)).toEqual(new Set(list)) + }) })