{"version":3,"file":"groupBy.cjs","names":["purry"],"sources":["../src/groupBy.ts"],"sourcesContent":["import type { BoundedPartial } from \"./internal/types/BoundedPartial\";\nimport type { NonEmptyArray } from \"./internal/types/NonEmptyArray\";\nimport { purry } from \"./purry\";\n\n/**\n * Groups the elements of a given iterable according to the string values\n * returned by a provided callback function. The returned object has separate\n * properties for each group, containing arrays with the elements in the group.\n * Unlike the built in `Object.groupBy` this function also allows the callback to\n * return `undefined` in order to exclude the item from being added to any\n * group.\n *\n * If you are grouping objects by a property of theirs (e.g.\n * `groupBy(data, ({ myProp }) => myProp)` or `groupBy(data, prop('myProp'))`)\n * consider using `groupByProp` (e.g. `groupByProp(data, 'myProp')`) instead,\n * as it would provide better typing.\n *\n * @param data - The items to group.\n * @param callbackfn - A function to execute for each element in the iterable.\n * It should return a value indicating the group of the current element, or\n * `undefined` when the item should be excluded from any group.\n * @returns An object with properties for all groups, each assigned to an array\n * containing the elements of the associated group.\n * @signature\n *    groupBy(data, callbackfn)\n * @example\n *    groupBy([{a: 'cat'}, {a: 'dog'}] as const, prop('a')) // => {cat: [{a: 'cat'}], dog: [{a: 'dog'}]}\n *    groupBy([0, 1], x => x % 2 === 0 ? 'even' : undefined) // => {even: [0]}\n * @dataFirst\n * @category Array\n */\nexport function groupBy<T, Key extends PropertyKey = PropertyKey>(\n  data: readonly T[],\n  callbackfn: (value: T, index: number, data: readonly T[]) => Key | undefined,\n): BoundedPartial<Record<Key, NonEmptyArray<T>>>;\n\n/**\n * Groups the elements of a given iterable according to the string values\n * returned by a provided callback function. The returned object has separate\n * properties for each group, containing arrays with the elements in the group.\n * Unlike the built in `Object.groupBy` this function also allows the callback to\n * return `undefined` in order to exclude the item from being added to any\n * group.\n *\n * If you are grouping objects by a property of theirs (e.g.\n * `groupBy(data, ({ myProp }) => myProp)` or `groupBy(data, prop('myProp'))`)\n * consider using `groupByProp` (e.g. `groupByProp(data, 'myProp')`) instead,\n * as it would provide better typing.\n *\n * @param callbackfn - A function to execute for each element in the iterable.\n * It should return a value indicating the group of the current element, or\n * `undefined` when the item should be excluded from any group.\n * @returns An object with properties for all groups, each assigned to an array\n * containing the elements of the associated group.\n * @signature\n *    groupBy(callbackfn)(data);\n * @example\n *    pipe(\n *      [{a: 'cat'}, {a: 'dog'}] as const,\n *      groupBy(prop('a')),\n *    ); // => {cat: [{a: 'cat'}], dog: [{a: 'dog'}]}\n *    pipe(\n *      [0, 1],\n *      groupBy(x => x % 2 === 0 ? 'even' : undefined),\n *    ); // => {even: [0]}\n * @dataLast\n * @category Array\n */\nexport function groupBy<T, Key extends PropertyKey = PropertyKey>(\n  callbackfn: (value: T, index: number, data: readonly T[]) => Key | undefined,\n): (items: readonly T[]) => BoundedPartial<Record<Key, NonEmptyArray<T>>>;\n\nexport function groupBy(...args: readonly unknown[]): unknown {\n  return purry(groupByImplementation, args);\n}\n\nconst groupByImplementation = <T, Key extends PropertyKey = PropertyKey>(\n  data: readonly T[],\n  callbackfn: (value: T, index: number, data: readonly T[]) => Key | undefined,\n): BoundedPartial<Record<Key, NonEmptyArray<T>>> => {\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Using Object.create(null) allows us to remove everything from the prototype chain, leaving it as a pure object that only has the keys *we* add to it. This prevents issues like the one raised in #1046\n  const output: BoundedPartial<Record<Key, NonEmptyArray<T>>> =\n    Object.create(null);\n\n  for (let index = 0; index < data.length; index++) {\n    // Accessing the object directly instead of via an iterator on the `entries` showed significant performance benefits while benchmarking.\n    const item = data[index];\n\n    // @ts-expect-error [ts2345] -- TypeScript is not able to infer that the index wouldn't overflow the array and that it shouldn't add `undefined` to the type. We don't want to use the `!` operator here because it's semantics are different because it changes the type of `item` to `NonNullable<T>` which is inaccurate because T itself could have `undefined` as a valid value.\n    const key = callbackfn(item, index, data);\n    if (key !== undefined) {\n      // Once the prototype chain is fixed, it is safe to access the prop directly without needing to check existence or types.\n      const items = output[key];\n\n      if (items === undefined) {\n        // It is more performant to create a 1-element array over creating an empty array and falling through to a unified the push. It is also more performant to mutate the existing object over using spread to continually create new objects on every unique key.\n        // @ts-expect-error [ts2322] -- In addition to the typing issue we have for `item`, this line also creates a typing issue for the whole object, as TypeScript is having a hard time inferring what values could be adding to the object.\n        output[key] = [item];\n      } else {\n        // It is more performant to add the items to an existing array over continually creating a new array every time we add an item to it.\n        // @ts-expect-error [ts2345] -- See comment above about the effective typing for `item` here.\n        items.push(item);\n      }\n    }\n  }\n\n  // Set the prototype as if we initialized our object as a normal object (e.g. `{}`). Without this none of the built-in object methods like `toString` would work on this object and it would act differently than expected.\n  Object.setPrototypeOf(output, Object.prototype);\n\n  return output;\n};\n"],"mappings":"kGAwEA,SAAgB,EAAQ,GAAG,EAAmC,CAC5D,OAAOA,EAAAA,MAAM,EAAuB,EAAK,CAG3C,MAAM,GACJ,EACA,IACkD,CAElD,IAAM,EACJ,OAAO,OAAO,KAAK,CAErB,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IAAS,CAEhD,IAAM,EAAO,EAAK,GAGZ,EAAM,EAAW,EAAM,EAAO,EAAK,CACzC,GAAI,IAAQ,IAAA,GAAW,CAErB,IAAM,EAAQ,EAAO,GAEjB,IAAU,IAAA,GAGZ,EAAO,GAAO,CAAC,EAAK,CAIpB,EAAM,KAAK,EAAK,EAQtB,OAFA,OAAO,eAAe,EAAQ,OAAO,UAAU,CAExC"}