Repository: faker-js/faker (TypeScript)

Issue:
Add support for count ranges in helpers.arrayElements
### Clear and concise description of the problem

Most of our methods that allow specifying a length or count allow specifying them as range.

E.g. https://next.fakerjs.dev/api/helpers.html#multiple

But `helpers.arrayElements` does not.

https://next.fakerjs.dev/api/helpers.html#arrayelements

### Suggested solution

Add support for `count: number, {min: number, max: number}` using [`helpers.rangeToNumber()`](https://next.fakerjs.dev/api/helpers.html#rangetonumber).

### Alternative

_No response_

### Additional context

Found in https://github.com/faker-js/faker/issues/1765


Discussion:
I can take this one



Patch:
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<T> = ['a', 'b', 'c'] as unknown as ReadonlyArray<T>,
-    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;
 


Test patch:
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([]);