diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index 8492a096ee4..a4ecdf71c06 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -561,13 +561,14 @@ export class HelpersModule { * * @template T The type of the entries to pick from. * @param array Array to pick the value from. - * @param count Number of elements to pick. + * @param count Number or range of elements to pick. * When not provided, random number of elements will be picked. * When value exceeds array boundaries, it will be limited to stay inside. * * @example * faker.helpers.arrayElements(['cat', 'dog', 'mouse']) // ['mouse', 'cat'] * faker.helpers.arrayElements([1, 2, 3, 4, 5], 2) // [4, 2] + * faker.helpers.arrayElements([1, 2, 3, 4, 5], { min: 2, max: 4 }) // [3, 5, 1] * * @since 6.3.0 */ @@ -575,22 +576,36 @@ export class HelpersModule { // TODO @Shinigami92 2022-04-30: We want to remove this default value, but currently it's not possible because some definitions could be empty // See https://github.com/faker-js/faker/issues/893 array: ReadonlyArray = ['a', 'b', 'c'] as unknown as ReadonlyArray, - count?: number + count?: + | number + | { + /** + * The minimum number of elements to pick. + */ + min: number; + /** + * The maximum number of elements to pick. + */ + max: number; + } ): T[] { - if (typeof count !== 'number') { - count = - array.length === 0 - ? 0 - : this.faker.number.int({ min: 1, max: array.length }); - } else if (count > array.length) { - count = array.length; - } else if (count < 0) { - count = 0; + if (array.length === 0) { + return []; + } + + const numElements = this.rangeToNumber( + count ?? { min: 1, max: array.length } + ); + + if (numElements >= array.length) { + return this.shuffle(array); + } else if (numElements <= 0) { + return []; } const arrayCopy = array.slice(0); let i = array.length; - const min = i - count; + const min = i - numElements; let temp: T; let index: number; diff --git a/test/__snapshots__/helpers.spec.ts.snap b/test/__snapshots__/helpers.spec.ts.snap index ca5ae30a031..3b149cf65fa 100644 --- a/test/__snapshots__/helpers.spec.ts.snap +++ b/test/__snapshots__/helpers.spec.ts.snap @@ -29,6 +29,13 @@ exports[`helpers > 42 > arrayElements > with array and count 1`] = ` ] `; +exports[`helpers > 42 > arrayElements > with array and count range 1`] = ` +[ + "d", + "l", +] +`; + exports[`helpers > 42 > fake > with a dynamic template 1`] = `"my string: Cky2eiXX/J"`; exports[`helpers > 42 > fake > with a static template 1`] = `"my test string"`; @@ -218,6 +225,16 @@ exports[`helpers > 1211 > arrayElements > with array and count 1`] = ` ] `; +exports[`helpers > 1211 > arrayElements > with array and count range 1`] = ` +[ + "e", + "l", + "o", + "l", + " ", +] +`; + exports[`helpers > 1211 > fake > with a dynamic template 1`] = `"my string: wKti5-}$_/"`; exports[`helpers > 1211 > fake > with a static template 1`] = `"my test string"`; @@ -403,6 +420,13 @@ exports[`helpers > 1337 > arrayElements > with array and count 1`] = ` ] `; +exports[`helpers > 1337 > arrayElements > with array and count range 1`] = ` +[ + "e", + "W", +] +`; + exports[`helpers > 1337 > fake > with a dynamic template 1`] = `"my string: 9U/4:SK$>6"`; exports[`helpers > 1337 > fake > with a static template 1`] = `"my test string"`; diff --git a/test/helpers.spec.ts b/test/helpers.spec.ts index 0e75e10fa80..b991cbac7f2 100644 --- a/test/helpers.spec.ts +++ b/test/helpers.spec.ts @@ -76,7 +76,11 @@ describe('helpers', () => { t.describe('arrayElements', (t) => { t.it('noArgs') .it('with array', 'Hello World!'.split('')) - .it('with array and count', 'Hello World!'.split(''), 3); + .it('with array and count', 'Hello World!'.split(''), 3) + .it('with array and count range', 'Hello World!'.split(''), { + min: 1, + max: 5, + }); }); t.describe('shuffle', (t) => { @@ -281,6 +285,46 @@ describe('helpers', () => { expect(subset).toHaveLength(new Set(subset).size); }); + it('should return a subset with random elements in the array for a length range', () => { + const testArray = ['hello', 'to', 'you', 'my', 'friend']; + const subset = faker.helpers.arrayElements(testArray, { + min: 2, + max: 4, + }); + + // Check length + expect(subset.length).toBeGreaterThanOrEqual(2); + expect(subset.length).toBeLessThanOrEqual(4); + + // Check elements + subset.forEach((element) => { + expect(testArray).toContain(element); + }); + + // Check uniqueness + expect(subset).not.toContainDuplicates(); + }); + + it('should return an array with all elements when count > array length', () => { + const testArray = ['hello', 'to', 'you', 'my', 'friend']; + const subset = faker.helpers.arrayElements(testArray, 6); + + // Check length + expect(subset.length).toEqual(5); + + // Check elements + subset.forEach((element) => { + expect(testArray).toContain(element); + }); + }); + + it('should return an empty array when array length > 0 and count = 0', () => { + const testArray = ['hello', 'to', 'you', 'my', 'friend']; + const result = faker.helpers.arrayElements(testArray, 0); + + expect(result).toHaveLength(0); + }); + it('should return an empty array when receiving an empty array', () => { const result = faker.helpers.arrayElements([]);