@oscarpalmer/atoms 0.165.2 → 0.166.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,10 +15,11 @@ import { ArrayPosition, endsWithArray, getArrayPosition, includesArray, indexOfA
15
15
  import { push } from "./push.mjs";
16
16
  import { select } from "./select.mjs";
17
17
  import { drop, slice, take } from "./slice.mjs";
18
- import { sort } from "./sort.mjs";
18
+ import { ArrayComparisonSorter, ArrayKeySorter, ArrayValueSorter, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SortDirection, sort } from "./sort.mjs";
19
19
  import { splice } from "./splice.mjs";
20
20
  import { toSet } from "./to-set.mjs";
21
21
  import { toggle } from "./toggle.mjs";
22
22
  import { union } from "./union.mjs";
23
+ import { unique } from "./unique.mjs";
23
24
  import { update } from "./update.mjs";
24
- export { ArrayPosition, chunk, compact, difference, drop, endsWithArray, exists, find, flatten, getArray, getArrayPosition, includesArray, indexOf, indexOfArray, insert, intersection, partition, push, range, select, shuffle, slice, sort, splice, startsWithArray, take, times, toSet, toggle, union, update };
25
+ export { ArrayComparisonSorter, ArrayKeySorter, ArrayPosition, ArrayValueSorter, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SortDirection, chunk, compact, difference, drop, endsWithArray, exists, find, flatten, getArray, getArrayPosition, includesArray, indexOf, indexOfArray, insert, intersection, partition, push, range, select, shuffle, slice, sort, splice, startsWithArray, take, times, toSet, toggle, union, unique, update };
@@ -15,10 +15,11 @@ import { endsWithArray, getArrayPosition, includesArray, indexOfArray, startsWit
15
15
  import { push } from "./push.mjs";
16
16
  import { select } from "./select.mjs";
17
17
  import { drop, slice, take } from "./slice.mjs";
18
- import { sort } from "./sort.mjs";
18
+ import { SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, sort } from "./sort.mjs";
19
19
  import { splice } from "./splice.mjs";
20
20
  import { toSet } from "./to-set.mjs";
21
21
  import { toggle } from "./toggle.mjs";
22
22
  import { union } from "./union.mjs";
23
+ import { unique } from "./unique.mjs";
23
24
  import { update } from "./update.mjs";
24
- export { chunk, compact, difference, drop, endsWithArray, exists, find, flatten, getArray, getArrayPosition, includesArray, indexOf, indexOfArray, insert, intersection, partition, push, range, select, shuffle, slice, sort, splice, startsWithArray, take, times, toSet, toggle, union, update };
25
+ export { SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, chunk, compact, difference, drop, endsWithArray, exists, find, flatten, getArray, getArrayPosition, includesArray, indexOf, indexOfArray, insert, intersection, partition, push, range, select, shuffle, slice, sort, splice, startsWithArray, take, times, toSet, toggle, union, unique, update };
@@ -2,30 +2,26 @@ import { PlainObject, Primitive } from "../models.mjs";
2
2
 
3
3
  //#region src/array/sort.d.ts
4
4
  /**
5
- * Sorting information for arrays _(using a callback)_
5
+ * Sorting information for arrays _(using a comparison callback)_
6
6
  */
7
- type ArrayCallbackSorter<Item> = {
7
+ type ArrayComparisonSorter<Item> = {
8
8
  /**
9
- * Comparator to use when comparing items and values
9
+ * Callback to use when comparing items and values
10
10
  */
11
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
11
+ comparison: ComparisonSorter<Item>;
12
12
  /**
13
13
  * Direction to sort by
14
14
  */
15
15
  direction?: SortDirection;
16
- /**
17
- * Value to sort by
18
- */
19
- value: (item: Item) => unknown;
20
16
  };
21
17
  /**
22
18
  * Sorting information for arrays _(using a key)_
23
19
  */
24
- type ArrayKeySorter<Item> = {
20
+ type ArrayKeySorter<Item extends PlainObject, Key extends keyof Item> = {
25
21
  /**
26
22
  * Comparator to use when comparing items and values
27
23
  */
28
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
24
+ compare?: CompareCallback<Item, Item[Key]>;
29
25
  /**
30
26
  * Direction to sort by
31
27
  */
@@ -33,41 +29,42 @@ type ArrayKeySorter<Item> = {
33
29
  /**
34
30
  * Key to sort by
35
31
  */
36
- key: keyof Item;
32
+ key: Key;
37
33
  };
38
- type SortDirection = 'ascending' | 'descending';
39
34
  /**
40
- * Sort an array of items, using multiple sorters to sort by specific values
41
- * @param array Array to sort
42
- * @param sorters Sorters to use for sorting
43
- * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
44
- * @returns Sorted array
35
+ * Sorters based on keys in an object
45
36
  */
46
- declare function sort<Item>(array: Item[], sorters: Array<((item: Item) => unknown) | ArrayCallbackSorter<Item>>, descending?: boolean): Item[];
37
+ type ArrayKeySorters<Item extends PlainObject> = { [Key in keyof Item]: ArrayKeySorter<Item, Key> }[keyof Item];
47
38
  /**
48
- * Sort an array of items, using a sorter to sort by a specific value
49
- * @param array Array to sort
50
- * @param sorter Sorter to use for sorting
51
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
52
- * @returns Sorted array
39
+ * Sorting information for arrays _(using a value callback and built-in comparison)_
40
+ */
41
+ type ArrayValueSorter<Item> = {
42
+ /**
43
+ * Direction to sort by
44
+ */
45
+ direction?: SortDirection;
46
+ /**
47
+ * Value to sort by
48
+ */
49
+ value: (item: Item) => unknown;
50
+ };
51
+ /**
52
+ * Comparator to use when comparing items and values
53
53
  */
54
- declare function sort<Item>(array: Item[], sorter: ((item: Item) => unknown) | ArrayCallbackSorter<Item>, descending?: boolean): Item[];
54
+ type CompareCallback<Item, Value = CompareCallbackValue<Item>> = (first: Item, firstValue: Value, second: Item, secondValue: Value) => number;
55
+ type CompareCallbackValue<Item> = Item extends Primitive ? Item : unknown;
55
56
  /**
56
- * Sort an array of items, using multiple sorters to sort by specific values
57
- * @param array Array to sort
58
- * @param sorters Sorters to use for sorting
59
- * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
60
- * @returns Sorted array
57
+ * Callback to use when comparing items and values
61
58
  */
62
- declare function sort<Item extends PlainObject>(array: Item[], sorters: Array<keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>>, descending?: boolean): Item[];
59
+ type ComparisonSorter<Item> = (first: Item, second: Item) => number;
63
60
  /**
64
- * Sort an array of items, using a sorter to sort by a specific value
65
- * @param array Array to sort
66
- * @param sorter Sorter to use for sorting
67
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
68
- * @returns Sorted array
61
+ * Direction to sort by
69
62
  */
70
- declare function sort<Item extends PlainObject>(array: Item[], sorter: keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>, descending?: boolean): Item[];
63
+ type SortDirection = 'ascending' | 'descending';
64
+ /**
65
+ * Sorter to use for sorting
66
+ */
67
+ type Sorter<Item> = Item extends PlainObject ? keyof Item | ArrayComparisonSorter<Item> | ArrayKeySorters<Item> | ArrayValueSorter<Item> | ComparisonSorter<Item> : ArrayComparisonSorter<Item> | ArrayValueSorter<Item> | ComparisonSorter<Item>;
71
68
  /**
72
69
  * Sort an array of items, using multiple sorters to sort by specific values
73
70
  * @param array Array to sort
@@ -75,15 +72,15 @@ declare function sort<Item extends PlainObject>(array: Item[], sorter: keyof Ite
75
72
  * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
76
73
  * @returns Sorted array
77
74
  */
78
- declare function sort<Item extends Primitive>(array: Item[], sorters: ((item: Item) => unknown)[], descending?: boolean): Item[];
75
+ declare function sort<Item>(array: Item[], sorters: Array<Sorter<Item>>, descending?: boolean): Item[];
79
76
  /**
80
- * Sort an array of items, using a sorter to sort by a specific value
77
+ * Sort an array of items, using multiple sorters to sort by specific values
81
78
  * @param array Array to sort
82
79
  * @param sorter Sorter to use for sorting
83
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
80
+ * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
84
81
  * @returns Sorted array
85
82
  */
86
- declare function sort<Item extends Primitive>(array: Item[], sorter: (item: Item) => unknown, descending?: boolean): Item[];
83
+ declare function sort<Item>(array: Item[], sorter: Sorter<Item>, descending?: boolean): Item[];
87
84
  /**
88
85
  * Sort an array of items
89
86
  * @param array Array to sort
@@ -94,4 +91,4 @@ declare function sort<Item>(array: Item[], descending?: boolean): Item[];
94
91
  declare const SORT_DIRECTION_ASCENDING: SortDirection;
95
92
  declare const SORT_DIRECTION_DESCENDING: SortDirection;
96
93
  //#endregion
97
- export { ArrayCallbackSorter, ArrayKeySorter, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SortDirection, sort };
94
+ export { ArrayComparisonSorter, ArrayKeySorter, ArrayValueSorter, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SortDirection, sort };
@@ -1,64 +1,71 @@
1
1
  import { isPlainObject } from "../internal/is.mjs";
2
2
  import { compare } from "../internal/value/compare.mjs";
3
3
  //#region src/array/sort.ts
4
- function getCallback(value, key, forObject) {
5
- if (key != null) return;
6
- if (forObject && typeof value.value === "function") return value.value;
7
- return typeof value === "function" ? value : void 0;
8
- }
9
- function getKey(value, forObject) {
10
- if (forObject && typeof value.key === "string") return value.key;
11
- return typeof value === "string" ? value : void 0;
4
+ function getComparisonSorter(callback, modifier) {
5
+ return {
6
+ modifier,
7
+ compare: { simple: callback },
8
+ get: false,
9
+ identifier: String(callback)
10
+ };
12
11
  }
13
- function getModifier(value, modifier, forObject) {
14
- if (!forObject || typeof value.direction !== "string") return modifier;
15
- if (value.direction === "ascending") return 1;
16
- return value.direction === "descending" ? -1 : modifier;
12
+ function getObjectSorter(obj, modifier) {
13
+ let sorter;
14
+ if (typeof obj.comparison === "function") sorter = getComparisonSorter(obj.comparison, modifier);
15
+ else if (typeof obj.key === "string") {
16
+ sorter = getValueSorter(obj.key, modifier);
17
+ if (typeof obj.compare === "function") sorter.compare = { complex: obj.compare };
18
+ } else if (typeof obj.value === "function") sorter = getValueSorter(obj.value, modifier);
19
+ if (sorter != null && typeof obj.direction === "string") sorter.modifier = modifiers[obj.direction] ?? modifier;
20
+ return sorter;
17
21
  }
18
22
  function getSorter(value, modifier) {
19
- const forObject = isPlainObject(value);
20
- const sorter = {
21
- identifier: "",
22
- modifier
23
- };
24
- sorter.compare = forObject && typeof value.compare === "function" ? value.compare : void 0;
25
- sorter.key = getKey(value, forObject);
26
- sorter.modifier = getModifier(value, modifier, forObject);
27
- sorter.callback = getCallback(value, sorter.key, forObject);
28
- if (sorter.key != null || sorter.callback != null) {
29
- sorter.identifier = `${sorter.key ?? sorter.callback}`;
30
- return sorter;
23
+ switch (true) {
24
+ case typeof value === "function": return getComparisonSorter(value, modifier);
25
+ case typeof value === "string": return getValueSorter(value, modifier);
26
+ case isPlainObject(value): return getObjectSorter(value, modifier);
27
+ default: break;
31
28
  }
32
29
  }
30
+ function getValueSorter(value, modifier) {
31
+ return {
32
+ modifier,
33
+ get: true,
34
+ identifier: String(value),
35
+ value: typeof value === "function" ? value : (item) => item[value]
36
+ };
37
+ }
33
38
  function sort(array, first, second) {
34
39
  if (!Array.isArray(array)) return [];
35
40
  if (array.length < 2) return array;
36
- const modifier = (first === true || second === true ? "descending" : "ascending") === "ascending" ? 1 : -1;
41
+ const modifier = modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
37
42
  const sorters = (Array.isArray(first) ? first : [first]).map((item) => getSorter(item, modifier)).filter((sorter) => sorter != null).filter((current, index, filtered) => filtered.findIndex((next) => next.identifier === current.identifier) === index);
38
43
  const { length } = sorters;
39
- if (length === 0) return array.sort((firstItem, secondItem) => compare(firstItem, secondItem) * modifier);
44
+ if (length === 0) return array.sort((first, second) => compare(first, second) * modifier);
40
45
  if (length === 1) {
41
46
  const sorter = sorters[0];
42
- const { callback, key } = sorter;
43
47
  return array.sort((firstItem, secondItem) => {
44
- const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
45
- const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
46
- return (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
48
+ const firstValue = sorter.get ? sorter.value(firstItem) : firstItem;
49
+ const secondValue = sorter.get ? sorter.value(secondItem) : secondItem;
50
+ return (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ?? sorter.compare?.simple?.(firstItem, secondItem) ?? compare(firstValue, secondValue)) * sorter.modifier;
47
51
  });
48
52
  }
49
53
  return array.sort((firstItem, secondItem) => {
50
54
  for (let index = 0; index < length; index += 1) {
51
55
  const sorter = sorters[index];
52
- const { callback, key } = sorter;
53
- const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
54
- const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
55
- const compared = (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
56
- if (compared !== 0) return compared;
56
+ const firstValue = sorter.value?.(firstItem) ?? firstItem;
57
+ const secondValue = sorter.value?.(secondItem) ?? secondItem;
58
+ const comparison = (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ?? sorter.compare?.simple?.(firstItem, secondItem) ?? compare(firstValue, secondValue)) * sorter.modifier;
59
+ if (comparison !== 0) return comparison;
57
60
  }
58
61
  return 0;
59
62
  });
60
63
  }
61
64
  const SORT_DIRECTION_ASCENDING = "ascending";
62
65
  const SORT_DIRECTION_DESCENDING = "descending";
66
+ const modifiers = {
67
+ [SORT_DIRECTION_ASCENDING]: 1,
68
+ [SORT_DIRECTION_DESCENDING]: -1
69
+ };
63
70
  //#endregion
64
71
  export { SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, sort };
package/dist/index.d.mts CHANGED
@@ -855,30 +855,26 @@ declare function take(array: unknown[], count: number): unknown[];
855
855
  //#endregion
856
856
  //#region src/array/sort.d.ts
857
857
  /**
858
- * Sorting information for arrays _(using a callback)_
858
+ * Sorting information for arrays _(using a comparison callback)_
859
859
  */
860
- type ArrayCallbackSorter<Item> = {
860
+ type ArrayComparisonSorter<Item> = {
861
861
  /**
862
- * Comparator to use when comparing items and values
862
+ * Callback to use when comparing items and values
863
863
  */
864
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
864
+ comparison: ComparisonSorter<Item>;
865
865
  /**
866
866
  * Direction to sort by
867
867
  */
868
868
  direction?: SortDirection;
869
- /**
870
- * Value to sort by
871
- */
872
- value: (item: Item) => unknown;
873
869
  };
874
870
  /**
875
871
  * Sorting information for arrays _(using a key)_
876
872
  */
877
- type ArrayKeySorter<Item> = {
873
+ type ArrayKeySorter<Item extends PlainObject, Key extends keyof Item> = {
878
874
  /**
879
875
  * Comparator to use when comparing items and values
880
876
  */
881
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
877
+ compare?: CompareCallback<Item, Item[Key]>;
882
878
  /**
883
879
  * Direction to sort by
884
880
  */
@@ -886,41 +882,42 @@ type ArrayKeySorter<Item> = {
886
882
  /**
887
883
  * Key to sort by
888
884
  */
889
- key: keyof Item;
885
+ key: Key;
890
886
  };
891
- type SortDirection = 'ascending' | 'descending';
892
887
  /**
893
- * Sort an array of items, using multiple sorters to sort by specific values
894
- * @param array Array to sort
895
- * @param sorters Sorters to use for sorting
896
- * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
897
- * @returns Sorted array
888
+ * Sorters based on keys in an object
898
889
  */
899
- declare function sort<Item>(array: Item[], sorters: Array<((item: Item) => unknown) | ArrayCallbackSorter<Item>>, descending?: boolean): Item[];
890
+ type ArrayKeySorters<Item extends PlainObject> = { [Key in keyof Item]: ArrayKeySorter<Item, Key> }[keyof Item];
900
891
  /**
901
- * Sort an array of items, using a sorter to sort by a specific value
902
- * @param array Array to sort
903
- * @param sorter Sorter to use for sorting
904
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
905
- * @returns Sorted array
892
+ * Sorting information for arrays _(using a value callback and built-in comparison)_
906
893
  */
907
- declare function sort<Item>(array: Item[], sorter: ((item: Item) => unknown) | ArrayCallbackSorter<Item>, descending?: boolean): Item[];
894
+ type ArrayValueSorter<Item> = {
895
+ /**
896
+ * Direction to sort by
897
+ */
898
+ direction?: SortDirection;
899
+ /**
900
+ * Value to sort by
901
+ */
902
+ value: (item: Item) => unknown;
903
+ };
908
904
  /**
909
- * Sort an array of items, using multiple sorters to sort by specific values
910
- * @param array Array to sort
911
- * @param sorters Sorters to use for sorting
912
- * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
913
- * @returns Sorted array
905
+ * Comparator to use when comparing items and values
914
906
  */
915
- declare function sort<Item extends PlainObject>(array: Item[], sorters: Array<keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>>, descending?: boolean): Item[];
907
+ type CompareCallback<Item, Value = CompareCallbackValue<Item>> = (first: Item, firstValue: Value, second: Item, secondValue: Value) => number;
908
+ type CompareCallbackValue<Item> = Item extends Primitive ? Item : unknown;
916
909
  /**
917
- * Sort an array of items, using a sorter to sort by a specific value
918
- * @param array Array to sort
919
- * @param sorter Sorter to use for sorting
920
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
921
- * @returns Sorted array
910
+ * Callback to use when comparing items and values
922
911
  */
923
- declare function sort<Item extends PlainObject>(array: Item[], sorter: keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>, descending?: boolean): Item[];
912
+ type ComparisonSorter<Item> = (first: Item, second: Item) => number;
913
+ /**
914
+ * Direction to sort by
915
+ */
916
+ type SortDirection = 'ascending' | 'descending';
917
+ /**
918
+ * Sorter to use for sorting
919
+ */
920
+ type Sorter<Item> = Item extends PlainObject ? keyof Item | ArrayComparisonSorter<Item> | ArrayKeySorters<Item> | ArrayValueSorter<Item> | ComparisonSorter<Item> : ArrayComparisonSorter<Item> | ArrayValueSorter<Item> | ComparisonSorter<Item>;
924
921
  /**
925
922
  * Sort an array of items, using multiple sorters to sort by specific values
926
923
  * @param array Array to sort
@@ -928,15 +925,15 @@ declare function sort<Item extends PlainObject>(array: Item[], sorter: keyof Ite
928
925
  * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
929
926
  * @returns Sorted array
930
927
  */
931
- declare function sort<Item extends Primitive>(array: Item[], sorters: ((item: Item) => unknown)[], descending?: boolean): Item[];
928
+ declare function sort<Item>(array: Item[], sorters: Array<Sorter<Item>>, descending?: boolean): Item[];
932
929
  /**
933
- * Sort an array of items, using a sorter to sort by a specific value
930
+ * Sort an array of items, using multiple sorters to sort by specific values
934
931
  * @param array Array to sort
935
932
  * @param sorter Sorter to use for sorting
936
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
933
+ * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
937
934
  * @returns Sorted array
938
935
  */
939
- declare function sort<Item extends Primitive>(array: Item[], sorter: (item: Item) => unknown, descending?: boolean): Item[];
936
+ declare function sort<Item>(array: Item[], sorter: Sorter<Item>, descending?: boolean): Item[];
940
937
  /**
941
938
  * Sort an array of items
942
939
  * @param array Array to sort
@@ -944,6 +941,8 @@ declare function sort<Item extends Primitive>(array: Item[], sorter: (item: Item
944
941
  * @returns Sorted array
945
942
  */
946
943
  declare function sort<Item>(array: Item[], descending?: boolean): Item[];
944
+ declare const SORT_DIRECTION_ASCENDING: SortDirection;
945
+ declare const SORT_DIRECTION_DESCENDING: SortDirection;
947
946
  //#endregion
948
947
  //#region src/array/splice.d.ts
949
948
  /**
@@ -1051,6 +1050,28 @@ declare function union<First extends Record<string, unknown>, Second extends Rec
1051
1050
  */
1052
1051
  declare function union<First, Second>(first: First[], second: Second[]): (First | Second)[];
1053
1052
  //#endregion
1053
+ //#region src/array/unique.d.ts
1054
+ /**
1055
+ * Get an array of unique items
1056
+ * @param array Original array
1057
+ * @param callback Callback to get an item's value
1058
+ * @returns Array of unique items
1059
+ */
1060
+ declare function unique<Item>(array: Item[], callback: (item: Item, index: number, array: Item[]) => unknown): Item[];
1061
+ /**
1062
+ * Get an array of unique items
1063
+ * @param array Original array
1064
+ * @param key Key to use for unique value
1065
+ * @returns Array of unique items
1066
+ */
1067
+ declare function unique<Item extends PlainObject, ItemKey extends keyof Item>(array: Item[], key: ItemKey): Item[];
1068
+ /**
1069
+ * Get an array of unique items
1070
+ * @param array Original array
1071
+ * @returns Array of unique items
1072
+ */
1073
+ declare function unique<Item>(array: Item[]): Item[];
1074
+ //#endregion
1054
1075
  //#region src/array/update.d.ts
1055
1076
  /**
1056
1077
  * Update an item in an array: if the item exists, it will be updated; if it doesn't, it will be added
@@ -1469,28 +1490,6 @@ declare function toRecordArrays<Item, Callback extends (item: Item, index: numbe
1469
1490
  */
1470
1491
  declare function toRecordArrays<Item extends PlainObject, ItemKey extends keyof Item>(array: Item[], key: ItemKey): Simplify<Record<KeyedValue<Item, ItemKey>, Item[]>>;
1471
1492
  //#endregion
1472
- //#region src/array/unique.d.ts
1473
- /**
1474
- * Get an array of unique items
1475
- * @param array Original array
1476
- * @param callback Callback to get an item's value
1477
- * @returns Array of unique items
1478
- */
1479
- declare function unique<Item>(array: Item[], callback: (item: Item, index: number, array: Item[]) => unknown): Item[];
1480
- /**
1481
- * Get an array of unique items
1482
- * @param array Original array
1483
- * @param key Key to use for unique value
1484
- * @returns Array of unique items
1485
- */
1486
- declare function unique<Item extends PlainObject, ItemKey extends keyof Item>(array: Item[], key: ItemKey): Item[];
1487
- /**
1488
- * Get an array of unique items
1489
- * @param array Original array
1490
- * @returns Array of unique items
1491
- */
1492
- declare function unique<Item>(array: Item[]): Item[];
1493
- //#endregion
1494
1493
  //#region src/function/assert.d.ts
1495
1494
  type Asserter<Value> = (value: unknown) => asserts value is Value;
1496
1495
  /**
@@ -4296,4 +4295,4 @@ declare class SizedSet<Value = unknown> extends Set<Value> {
4296
4295
  get(value: Value, update?: boolean): Value | undefined;
4297
4296
  }
4298
4297
  //#endregion
4299
- export { ArrayOrPlainObject, ArrayPosition, Asserter, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, type Err, EventPosition, type ExtendedErr, type ExtendedResult, Flow, FlowPromise, type FulfilledPromise, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, HasValue, Key$1 as Key, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NumericalKeys, NumericalValues, type Observable, type Observer, type Ok, OnceAsyncCallback, OnceCallback, PlainObject, Primitive, type PromiseOptions, type PromiseStrategy, PromiseTimeoutError, type PromisesOptions, type PromisesResult, type PromisesValues as PromisesValue, type PromisesValue as PromisesValueItem, type Queue, QueueError, type QueueOptions, type Queued, type RGBAColor, type RGBColor, type RejectedPromise, RequiredKeys, type Result, RetryError, RetryOptions, Simplify, SizedMap, SizedSet, Smushed, type Subscription, TemplateOptions, type Time, ToString, TypedArray, Unsmushed, assert, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
4298
+ export { ArrayComparisonSorter, ArrayKeySorter, ArrayOrPlainObject, ArrayPosition, ArrayValueSorter, Asserter, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, type Err, EventPosition, type ExtendedErr, type ExtendedResult, Flow, FlowPromise, type FulfilledPromise, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, HasValue, Key$1 as Key, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NumericalKeys, NumericalValues, type Observable, type Observer, type Ok, OnceAsyncCallback, OnceCallback, PlainObject, Primitive, type PromiseOptions, type PromiseStrategy, PromiseTimeoutError, type PromisesOptions, type PromisesResult, type PromisesValues as PromisesValue, type PromisesValue as PromisesValueItem, type Queue, QueueError, type QueueOptions, type Queued, type RGBAColor, type RGBColor, type RejectedPromise, RequiredKeys, type Result, RetryError, RetryOptions, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, Simplify, SizedMap, SizedSet, Smushed, SortDirection, type Subscription, TemplateOptions, type Time, ToString, TypedArray, Unsmushed, assert, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
package/dist/index.mjs CHANGED
@@ -762,63 +762,72 @@ const comparators = {
762
762
  };
763
763
  //#endregion
764
764
  //#region src/array/sort.ts
765
- function getCallback(value, key, forObject) {
766
- if (key != null) return;
767
- if (forObject && typeof value.value === "function") return value.value;
768
- return typeof value === "function" ? value : void 0;
769
- }
770
- function getKey(value, forObject) {
771
- if (forObject && typeof value.key === "string") return value.key;
772
- return typeof value === "string" ? value : void 0;
765
+ function getComparisonSorter(callback, modifier) {
766
+ return {
767
+ modifier,
768
+ compare: { simple: callback },
769
+ get: false,
770
+ identifier: String(callback)
771
+ };
773
772
  }
774
- function getModifier(value, modifier, forObject) {
775
- if (!forObject || typeof value.direction !== "string") return modifier;
776
- if (value.direction === "ascending") return 1;
777
- return value.direction === "descending" ? -1 : modifier;
773
+ function getObjectSorter(obj, modifier) {
774
+ let sorter;
775
+ if (typeof obj.comparison === "function") sorter = getComparisonSorter(obj.comparison, modifier);
776
+ else if (typeof obj.key === "string") {
777
+ sorter = getValueSorter(obj.key, modifier);
778
+ if (typeof obj.compare === "function") sorter.compare = { complex: obj.compare };
779
+ } else if (typeof obj.value === "function") sorter = getValueSorter(obj.value, modifier);
780
+ if (sorter != null && typeof obj.direction === "string") sorter.modifier = modifiers[obj.direction] ?? modifier;
781
+ return sorter;
778
782
  }
779
783
  function getSorter(value, modifier) {
780
- const forObject = isPlainObject(value);
781
- const sorter = {
782
- identifier: "",
783
- modifier
784
- };
785
- sorter.compare = forObject && typeof value.compare === "function" ? value.compare : void 0;
786
- sorter.key = getKey(value, forObject);
787
- sorter.modifier = getModifier(value, modifier, forObject);
788
- sorter.callback = getCallback(value, sorter.key, forObject);
789
- if (sorter.key != null || sorter.callback != null) {
790
- sorter.identifier = `${sorter.key ?? sorter.callback}`;
791
- return sorter;
784
+ switch (true) {
785
+ case typeof value === "function": return getComparisonSorter(value, modifier);
786
+ case typeof value === "string": return getValueSorter(value, modifier);
787
+ case isPlainObject(value): return getObjectSorter(value, modifier);
788
+ default: break;
792
789
  }
793
790
  }
791
+ function getValueSorter(value, modifier) {
792
+ return {
793
+ modifier,
794
+ get: true,
795
+ identifier: String(value),
796
+ value: typeof value === "function" ? value : (item) => item[value]
797
+ };
798
+ }
794
799
  function sort(array, first, second) {
795
800
  if (!Array.isArray(array)) return [];
796
801
  if (array.length < 2) return array;
797
- const modifier = (first === true || second === true ? "descending" : "ascending") === "ascending" ? 1 : -1;
802
+ const modifier = modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
798
803
  const sorters = (Array.isArray(first) ? first : [first]).map((item) => getSorter(item, modifier)).filter((sorter) => sorter != null).filter((current, index, filtered) => filtered.findIndex((next) => next.identifier === current.identifier) === index);
799
804
  const { length } = sorters;
800
- if (length === 0) return array.sort((firstItem, secondItem) => compare(firstItem, secondItem) * modifier);
805
+ if (length === 0) return array.sort((first, second) => compare(first, second) * modifier);
801
806
  if (length === 1) {
802
807
  const sorter = sorters[0];
803
- const { callback, key } = sorter;
804
808
  return array.sort((firstItem, secondItem) => {
805
- const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
806
- const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
807
- return (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
809
+ const firstValue = sorter.get ? sorter.value(firstItem) : firstItem;
810
+ const secondValue = sorter.get ? sorter.value(secondItem) : secondItem;
811
+ return (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ?? sorter.compare?.simple?.(firstItem, secondItem) ?? compare(firstValue, secondValue)) * sorter.modifier;
808
812
  });
809
813
  }
810
814
  return array.sort((firstItem, secondItem) => {
811
815
  for (let index = 0; index < length; index += 1) {
812
816
  const sorter = sorters[index];
813
- const { callback, key } = sorter;
814
- const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
815
- const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
816
- const compared = (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
817
- if (compared !== 0) return compared;
817
+ const firstValue = sorter.value?.(firstItem) ?? firstItem;
818
+ const secondValue = sorter.value?.(secondItem) ?? secondItem;
819
+ const comparison = (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ?? sorter.compare?.simple?.(firstItem, secondItem) ?? compare(firstValue, secondValue)) * sorter.modifier;
820
+ if (comparison !== 0) return comparison;
818
821
  }
819
822
  return 0;
820
823
  });
821
824
  }
825
+ const SORT_DIRECTION_ASCENDING = "ascending";
826
+ const SORT_DIRECTION_DESCENDING = "descending";
827
+ const modifiers = {
828
+ [SORT_DIRECTION_ASCENDING]: 1,
829
+ [SORT_DIRECTION_DESCENDING]: -1
830
+ };
822
831
  //#endregion
823
832
  //#region src/array/splice.ts
824
833
  function splice(array, start, deleteCountOrItems, items) {
@@ -868,6 +877,12 @@ function union(first, second, key) {
868
877
  return compareSets(COMPARE_SETS_UNION, first, second, key);
869
878
  }
870
879
  //#endregion
880
+ //#region src/array/unique.ts
881
+ function unique(array, key) {
882
+ if (!Array.isArray(array)) return [];
883
+ return array.length > 1 ? findValues(FIND_VALUES_UNIQUE, array, [key, void 0]).matched : array;
884
+ }
885
+ //#endregion
871
886
  //#region src/array/update.ts
872
887
  function update(array, values, key) {
873
888
  return updateInArray(array, values, key, true);
@@ -1043,12 +1058,6 @@ function toRecordArrays(array, first, second) {
1043
1058
  return groupValues(array, first, second, true);
1044
1059
  }
1045
1060
  //#endregion
1046
- //#region src/array/unique.ts
1047
- function unique(array, key) {
1048
- if (!Array.isArray(array)) return [];
1049
- return array.length > 1 ? findValues(FIND_VALUES_UNIQUE, array, [key, void 0]).matched : array;
1050
- }
1051
- //#endregion
1052
1061
  //#region src/function/assert.ts
1053
1062
  /**
1054
1063
  * Asserts that a condition is true, throwing an error if it is not
@@ -4426,4 +4435,4 @@ var SizedSet = class extends Set {
4426
4435
  }
4427
4436
  };
4428
4437
  //#endregion
4429
- export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, assert, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
4438
+ export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SizedMap, SizedSet, assert, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, endsWithArray, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getArrayPosition, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, includesArray, indexOf, indexOfArray, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, move, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/atoms",
3
- "version": "0.165.2",
3
+ "version": "0.166.1",
4
4
  "description": "Atomic utilities for making your JavaScript better.",
5
5
  "keywords": [
6
6
  "helper",
@@ -195,7 +195,7 @@
195
195
  "devDependencies": {
196
196
  "@types/node": "^25.5",
197
197
  "@vitest/coverage-istanbul": "^4.1",
198
- "jsdom": "^28.1",
198
+ "jsdom": "^29.0",
199
199
  "tsdown": "^0.21",
200
200
  "typescript": "^5.9",
201
201
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
@@ -16,9 +16,10 @@ export * from './position';
16
16
  export * from './push';
17
17
  export * from './select';
18
18
  export * from './slice';
19
- export {sort} from './sort';
19
+ export * from './sort';
20
20
  export * from './splice';
21
21
  export * from './to-set';
22
22
  export * from './toggle';
23
23
  export * from './union';
24
+ export * from './unique';
24
25
  export * from './update';
package/src/array/sort.ts CHANGED
@@ -1,35 +1,31 @@
1
1
  import {isPlainObject} from '../internal/is';
2
2
  import {compare} from '../internal/value/compare';
3
- import type {GenericCallback, PlainObject, Primitive} from '../models';
3
+ import type {PlainObject, Primitive} from '../models';
4
4
 
5
5
  // #region Types
6
6
 
7
7
  /**
8
- * Sorting information for arrays _(using a callback)_
8
+ * Sorting information for arrays _(using a comparison callback)_
9
9
  */
10
- export type ArrayCallbackSorter<Item> = {
10
+ export type ArrayComparisonSorter<Item> = {
11
11
  /**
12
- * Comparator to use when comparing items and values
12
+ * Callback to use when comparing items and values
13
13
  */
14
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
14
+ comparison: ComparisonSorter<Item>;
15
15
  /**
16
16
  * Direction to sort by
17
17
  */
18
18
  direction?: SortDirection;
19
- /**
20
- * Value to sort by
21
- */
22
- value: (item: Item) => unknown;
23
19
  };
24
20
 
25
21
  /**
26
22
  * Sorting information for arrays _(using a key)_
27
23
  */
28
- export type ArrayKeySorter<Item> = {
24
+ export type ArrayKeySorter<Item extends PlainObject, Key extends keyof Item> = {
29
25
  /**
30
26
  * Comparator to use when comparing items and values
31
27
  */
32
- compare?: (first: Item, firstValue: unknown, second: Item, secondValue: unknown) => number;
28
+ compare?: CompareCallback<Item, Item[Key]>;
33
29
  /**
34
30
  * Direction to sort by
35
31
  */
@@ -37,82 +33,150 @@ export type ArrayKeySorter<Item> = {
37
33
  /**
38
34
  * Key to sort by
39
35
  */
40
- key: keyof Item;
36
+ key: Key;
41
37
  };
42
38
 
43
- export type SortDirection = 'ascending' | 'descending';
39
+ /**
40
+ * Sorters based on keys in an object
41
+ */
42
+ type ArrayKeySorters<Item extends PlainObject> = {
43
+ [Key in keyof Item]: ArrayKeySorter<Item, Key>;
44
+ }[keyof Item];
45
+
46
+ /**
47
+ * Sorting information for arrays _(using a value callback and built-in comparison)_
48
+ */
49
+ export type ArrayValueSorter<Item> = {
50
+ /**
51
+ * Direction to sort by
52
+ */
53
+ direction?: SortDirection;
54
+ /**
55
+ * Value to sort by
56
+ */
57
+ value: (item: Item) => unknown;
58
+ };
44
59
 
45
- type Sorter = {
46
- callback?: (item: unknown) => unknown;
47
- compare?: (first: unknown, firstValue: unknown, second: unknown, secondValue: unknown) => number;
60
+ /**
61
+ * Comparator to use when comparing items and values
62
+ */
63
+ type CompareCallback<Item, Value = CompareCallbackValue<Item>> = (
64
+ first: Item,
65
+ firstValue: Value,
66
+ second: Item,
67
+ secondValue: Value,
68
+ ) => number;
69
+
70
+ type CompareCallbackValue<Item> = Item extends Primitive ? Item : unknown;
71
+
72
+ /**
73
+ * Callback to use when comparing items and values
74
+ */
75
+ type ComparisonSorter<Item> = (first: Item, second: Item) => number;
76
+
77
+ /**
78
+ * Internal sorter information
79
+ */
80
+ type InternalSorter = {
81
+ compare?: SorterCompare;
82
+ get: boolean;
48
83
  identifier: string;
49
- key?: string;
50
84
  modifier: number;
51
- sorter?: (first: unknown, firstValue: unknown, second: unknown, secondValue: unknown) => number;
85
+ value?: (item: PlainObject) => unknown;
52
86
  };
53
87
 
54
- // #endregion
88
+ /**
89
+ * Direction to sort by
90
+ */
91
+ export type SortDirection = 'ascending' | 'descending';
55
92
 
56
- // #region Functions
93
+ /**
94
+ * Comparison callbacks for sorter
95
+ */
96
+ type SorterCompare = {
97
+ complex?: Function;
98
+ simple?: Function;
99
+ };
57
100
 
58
- function getCallback(
59
- value: unknown,
60
- key: string | undefined,
61
- forObject: boolean,
62
- ): GenericCallback | undefined {
63
- if (key != null) {
64
- return;
65
- }
101
+ /**
102
+ * Sorter to use for sorting
103
+ */
104
+ type Sorter<Item> = Item extends PlainObject
105
+ ?
106
+ | keyof Item
107
+ | ArrayComparisonSorter<Item>
108
+ | ArrayKeySorters<Item>
109
+ | ArrayValueSorter<Item>
110
+ | ComparisonSorter<Item>
111
+ : ArrayComparisonSorter<Item> | ArrayValueSorter<Item> | ComparisonSorter<Item>;
66
112
 
67
- if (forObject && typeof (value as PlainObject).value === 'function') {
68
- return (value as PlainObject).value as never;
69
- }
113
+ // #endregion
70
114
 
71
- return typeof value === 'function' ? (value as never) : undefined;
115
+ // #region Functions
116
+
117
+ function getComparisonSorter(callback: Function, modifier: number): InternalSorter {
118
+ return {
119
+ modifier,
120
+ compare: {
121
+ simple: callback,
122
+ },
123
+ get: false,
124
+ identifier: String(callback),
125
+ };
72
126
  }
73
127
 
74
- function getKey(value: unknown, forObject: boolean): string | undefined {
75
- if (forObject && typeof (value as PlainObject).key === 'string') {
76
- return (value as PlainObject).key as string;
77
- }
128
+ function getObjectSorter(obj: PlainObject, modifier: number): InternalSorter | undefined {
129
+ let sorter: InternalSorter | undefined;
78
130
 
79
- return typeof value === 'string' ? value : undefined;
80
- }
131
+ if (typeof obj.comparison === 'function') {
132
+ sorter = getComparisonSorter(obj.comparison, modifier);
133
+ } else if (typeof obj.key === 'string') {
134
+ sorter = getValueSorter(obj.key, modifier);
81
135
 
82
- function getModifier(value: unknown, modifier: number, forObject: boolean): number {
83
- if (!forObject || typeof (value as PlainObject).direction !== 'string') {
84
- return modifier;
136
+ if (typeof obj.compare === 'function') {
137
+ sorter.compare = {
138
+ complex: obj.compare,
139
+ };
140
+ }
141
+ } else if (typeof obj.value === 'function') {
142
+ sorter = getValueSorter(obj.value, modifier);
85
143
  }
86
144
 
87
- if ((value as PlainObject).direction === SORT_DIRECTION_ASCENDING) {
88
- return 1;
145
+ if (sorter != null && typeof obj.direction === 'string') {
146
+ sorter.modifier = modifiers[obj.direction as SortDirection] ?? modifier;
89
147
  }
90
148
 
91
- return (value as PlainObject).direction === SORT_DIRECTION_DESCENDING ? -1 : modifier;
149
+ return sorter;
92
150
  }
93
151
 
94
- function getSorter(value: unknown, modifier: number): Sorter | undefined {
95
- const forObject = isPlainObject(value);
96
-
97
- const sorter: Sorter = {
98
- identifier: '',
99
- modifier,
100
- };
101
-
102
- sorter.compare =
103
- forObject && typeof value.compare === 'function' ? (value.compare as never) : undefined;
152
+ function getSorter(value: unknown, modifier: number): InternalSorter | undefined {
153
+ switch (true) {
154
+ case typeof value === 'function':
155
+ return getComparisonSorter(value, modifier);
104
156
 
105
- sorter.key = getKey(value, forObject);
106
- sorter.modifier = getModifier(value, modifier, forObject);
107
- sorter.callback = getCallback(value, sorter.key, forObject);
157
+ case typeof value === 'string':
158
+ return getValueSorter(value, modifier);
108
159
 
109
- if (sorter.key != null || sorter.callback != null) {
110
- sorter.identifier = `${sorter.key ?? sorter.callback}`;
160
+ case isPlainObject(value):
161
+ return getObjectSorter(value, modifier);
111
162
 
112
- return sorter;
163
+ default:
164
+ break;
113
165
  }
114
166
  }
115
167
 
168
+ function getValueSorter(value: string | Function, modifier: number): InternalSorter {
169
+ return {
170
+ modifier,
171
+ get: true,
172
+ identifier: String(value),
173
+ value:
174
+ typeof value === 'function'
175
+ ? (value as (item: PlainObject) => unknown)
176
+ : item => (item as PlainObject)[value],
177
+ };
178
+ }
179
+
116
180
  /**
117
181
  * Sort an array of items, using multiple sorters to sort by specific values
118
182
  * @param array Array to sort
@@ -122,76 +186,18 @@ function getSorter(value: unknown, modifier: number): Sorter | undefined {
122
186
  */
123
187
  export function sort<Item>(
124
188
  array: Item[],
125
- sorters: Array<((item: Item) => unknown) | ArrayCallbackSorter<Item>>,
126
- descending?: boolean,
127
- ): Item[];
128
-
129
- /**
130
- * Sort an array of items, using a sorter to sort by a specific value
131
- * @param array Array to sort
132
- * @param sorter Sorter to use for sorting
133
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
134
- * @returns Sorted array
135
- */
136
- export function sort<Item>(
137
- array: Item[],
138
- sorter: ((item: Item) => unknown) | ArrayCallbackSorter<Item>,
189
+ sorters: Array<Sorter<Item>>,
139
190
  descending?: boolean,
140
191
  ): Item[];
141
192
 
142
193
  /**
143
194
  * Sort an array of items, using multiple sorters to sort by specific values
144
195
  * @param array Array to sort
145
- * @param sorters Sorters to use for sorting
146
- * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
147
- * @returns Sorted array
148
- */
149
- export function sort<Item extends PlainObject>(
150
- array: Item[],
151
- sorters: Array<
152
- keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>
153
- >,
154
- descending?: boolean,
155
- ): Item[];
156
-
157
- /**
158
- * Sort an array of items, using a sorter to sort by a specific value
159
- * @param array Array to sort
160
196
  * @param sorter Sorter to use for sorting
161
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
162
- * @returns Sorted array
163
- */
164
- export function sort<Item extends PlainObject>(
165
- array: Item[],
166
- sorter: keyof Item | ((item: Item) => unknown) | ArrayCallbackSorter<Item> | ArrayKeySorter<Item>,
167
- descending?: boolean,
168
- ): Item[];
169
-
170
- /**
171
- * Sort an array of items, using multiple sorters to sort by specific values
172
- * @param array Array to sort
173
- * @param sorters Sorters to use for sorting
174
197
  * @param descending Sort in descending order? _(defaults to `false`; overridden by individual sorters)_
175
198
  * @returns Sorted array
176
199
  */
177
- export function sort<Item extends Primitive>(
178
- array: Item[],
179
- sorters: ((item: Item) => unknown)[],
180
- descending?: boolean,
181
- ): Item[];
182
-
183
- /**
184
- * Sort an array of items, using a sorter to sort by a specific value
185
- * @param array Array to sort
186
- * @param sorter Sorter to use for sorting
187
- * @param descending Sort in descending order? _(defaults to `false`; overridden by the sorter)_
188
- * @returns Sorted array
189
- */
190
- export function sort<Item extends Primitive>(
191
- array: Item[],
192
- sorter: (item: Item) => unknown,
193
- descending?: boolean,
194
- ): Item[];
200
+ export function sort<Item>(array: Item[], sorter: Sorter<Item>, descending?: boolean): Item[];
195
201
 
196
202
  /**
197
203
  * Sort an array of items
@@ -213,7 +219,7 @@ export function sort(array: unknown[], first?: unknown, second?: unknown): unkno
213
219
  const direction =
214
220
  first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING;
215
221
 
216
- const modifier = direction === SORT_DIRECTION_ASCENDING ? 1 : -1;
222
+ const modifier = modifiers[direction];
217
223
 
218
224
  const sorters = (Array.isArray(first) ? first : [first])
219
225
  .map(item => getSorter(item, modifier))
@@ -226,21 +232,19 @@ export function sort(array: unknown[], first?: unknown, second?: unknown): unkno
226
232
  const {length} = sorters;
227
233
 
228
234
  if (length === 0) {
229
- return array.sort(
230
- (firstItem, secondItem) => compare(firstItem as never, secondItem as never) * modifier,
231
- );
235
+ return array.sort((first, second) => compare(first, second) * modifier);
232
236
  }
233
237
 
234
238
  if (length === 1) {
235
239
  const sorter = sorters[0];
236
- const {callback, key} = sorter;
237
240
 
238
241
  return array.sort((firstItem, secondItem) => {
239
- const firstValue = key == null ? callback?.(firstItem) : (firstItem as PlainObject)[key];
240
- const secondValue = key == null ? callback?.(secondItem) : (secondItem as PlainObject)[key];
242
+ const firstValue = sorter.get ? sorter.value!(firstItem as PlainObject) : firstItem;
243
+ const secondValue = sorter.get ? sorter.value!(secondItem as PlainObject) : secondItem;
241
244
 
242
245
  return (
243
- (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ??
246
+ (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ??
247
+ sorter.compare?.simple?.(firstItem, secondItem) ??
244
248
  compare(firstValue, secondValue)) * sorter.modifier
245
249
  );
246
250
  });
@@ -249,17 +253,17 @@ export function sort(array: unknown[], first?: unknown, second?: unknown): unkno
249
253
  return array.sort((firstItem, secondItem) => {
250
254
  for (let index = 0; index < length; index += 1) {
251
255
  const sorter = sorters[index];
252
- const {callback, key} = sorter;
253
256
 
254
- const firstValue = key == null ? callback?.(firstItem) : (firstItem as PlainObject)[key];
255
- const secondValue = key == null ? callback?.(secondItem) : (secondItem as PlainObject)[key];
257
+ const firstValue = sorter.value?.(firstItem as PlainObject) ?? firstItem;
258
+ const secondValue = sorter.value?.(secondItem as PlainObject) ?? secondItem;
256
259
 
257
- const compared =
258
- (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ??
260
+ const comparison =
261
+ (sorter.compare?.complex?.(firstItem, firstValue, secondItem, secondValue) ??
262
+ sorter.compare?.simple?.(firstItem, secondItem) ??
259
263
  compare(firstValue, secondValue)) * sorter.modifier;
260
264
 
261
- if (compared !== 0) {
262
- return compared;
265
+ if (comparison !== 0) {
266
+ return comparison;
263
267
  }
264
268
  }
265
269
 
@@ -275,4 +279,9 @@ export const SORT_DIRECTION_ASCENDING: SortDirection = 'ascending';
275
279
 
276
280
  export const SORT_DIRECTION_DESCENDING: SortDirection = 'descending';
277
281
 
282
+ const modifiers: Record<SortDirection, number> = {
283
+ [SORT_DIRECTION_ASCENDING]: 1,
284
+ [SORT_DIRECTION_DESCENDING]: -1,
285
+ };
286
+
278
287
  // #endregion