@oscarpalmer/atoms 0.180.0 → 0.182.0

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.
Files changed (46) hide show
  1. package/dist/array/find.d.mts +42 -1
  2. package/dist/array/find.mjs +5 -1
  3. package/dist/array/get.mjs +2 -2
  4. package/dist/array/index.d.mts +3 -3
  5. package/dist/array/index.mjs +3 -3
  6. package/dist/index.d.mts +238 -25
  7. package/dist/index.mjs +242 -19
  8. package/dist/internal/array/find.d.mts +4 -3
  9. package/dist/internal/array/find.mjs +5 -2
  10. package/dist/internal/array/index-of.d.mts +42 -1
  11. package/dist/internal/array/index-of.mjs +5 -1
  12. package/dist/internal/math/aggregate.mjs +2 -2
  13. package/dist/internal/result.mjs +2 -2
  14. package/dist/internal/string.d.mts +5 -3
  15. package/dist/internal/string.mjs +14 -5
  16. package/dist/internal/value/equal.mjs +2 -2
  17. package/dist/internal/value/handlers.mjs +2 -2
  18. package/dist/kalas.d.mts +61 -0
  19. package/dist/kalas.mjs +93 -0
  20. package/dist/string/case.mjs +10 -4
  21. package/dist/string/normalize.d.mts +55 -0
  22. package/dist/string/normalize.mjs +93 -0
  23. package/dist/value/index.d.mts +2 -1
  24. package/dist/value/index.mjs +2 -1
  25. package/dist/value/merge.d.mts +13 -0
  26. package/dist/value/merge.mjs +3 -1
  27. package/dist/value/shake.d.mts +7 -0
  28. package/dist/value/shake.mjs +16 -0
  29. package/package.json +1 -1
  30. package/src/array/exists.ts +1 -1
  31. package/src/array/find.ts +58 -0
  32. package/src/array/get.ts +2 -2
  33. package/src/index.ts +3 -0
  34. package/src/internal/array/find.ts +24 -4
  35. package/src/internal/array/index-of.ts +59 -1
  36. package/src/internal/math/aggregate.ts +2 -2
  37. package/src/internal/result.ts +6 -3
  38. package/src/internal/string.ts +26 -8
  39. package/src/internal/value/equal.ts +2 -2
  40. package/src/internal/value/handlers.ts +2 -2
  41. package/src/kalas.ts +167 -0
  42. package/src/string/case.ts +20 -4
  43. package/src/string/normalize.ts +169 -0
  44. package/src/value/index.ts +1 -0
  45. package/src/value/merge.ts +17 -1
  46. package/src/value/shake.ts +36 -0
@@ -0,0 +1,61 @@
1
+ import { GenericCallback } from "./models.mjs";
2
+
3
+ //#region src/kalas.d.ts
4
+ declare class Events<Map extends Record<string, GenericCallback>> {
5
+ #private;
6
+ constructor(kalas: Kalas<Map>);
7
+ /**
8
+ * Subscribe to an event with a callback
9
+ * @param event Event name
10
+ * @param callback Callback function
11
+ * @returns Unsubscriber function
12
+ */
13
+ subscribe<Event extends keyof Map>(event: Event, callback: Map[Event]): Unsubscriber;
14
+ /**
15
+ * Unsubscribe from an event with a callback _(or all callbacks, if no callback is provided)_
16
+ * @param event Event name
17
+ * @param callback Callback function
18
+ * @returns Unsubscriber function
19
+ */
20
+ unsubscribe<Event extends keyof Map>(event: Event, callback?: Map[Event]): void;
21
+ }
22
+ declare class Kalas<Map extends Record<string, GenericCallback>> {
23
+ #private;
24
+ /**
25
+ * Events interface for subscribing and unsubscribing to events
26
+ */
27
+ readonly events: Events<Map>;
28
+ constructor(names: (keyof Map)[]);
29
+ /**
30
+ * Remove all event subscribers
31
+ */
32
+ clear(): void;
33
+ /**
34
+ * Emit an event with parameters
35
+ * @param event Event name
36
+ * @param parameters Event parameters
37
+ */
38
+ emit<Event extends keyof Map>(event: Event, ...parameters: Parameters<Map[Event]>): void;
39
+ /**
40
+ * Subscribe to an event with a callback
41
+ * @param event Event name
42
+ * @param callback Callback function
43
+ * @returns Unsubscriber function
44
+ */
45
+ subscribe<Event extends keyof Map>(event: Event, callback: Map[Event]): Unsubscriber;
46
+ /**
47
+ * Unsubscribe from an event with a callback _(or all callbacks, if no callback is provided)_
48
+ * @param event Event name
49
+ * @param callback Callback function
50
+ */
51
+ unsubscribe<Event extends keyof Map>(event: Event, callback?: Map[Event]): void;
52
+ }
53
+ type Unsubscriber = () => void;
54
+ /**
55
+ * Create a Kalas _(party)_ for named events
56
+ * @param names Event names
57
+ * @returns Kalas instance
58
+ */
59
+ declare function kalas<Events extends Record<string, GenericCallback>>(names: (keyof Events)[]): Kalas<Events>;
60
+ //#endregion
61
+ export { type Events, type Kalas, Unsubscriber, kalas };
package/dist/kalas.mjs ADDED
@@ -0,0 +1,93 @@
1
+ import { noop } from "./internal/function/misc.mjs";
2
+ //#region src/kalas.ts
3
+ var Events = class {
4
+ #kalas;
5
+ constructor(kalas) {
6
+ this.#kalas = kalas;
7
+ }
8
+ /**
9
+ * Subscribe to an event with a callback
10
+ * @param event Event name
11
+ * @param callback Callback function
12
+ * @returns Unsubscriber function
13
+ */
14
+ subscribe(event, callback) {
15
+ return this.#kalas.subscribe(event, callback);
16
+ }
17
+ /**
18
+ * Unsubscribe from an event with a callback _(or all callbacks, if no callback is provided)_
19
+ * @param event Event name
20
+ * @param callback Callback function
21
+ * @returns Unsubscriber function
22
+ */
23
+ unsubscribe(event, callback) {
24
+ return this.#kalas.unsubscribe(event, callback);
25
+ }
26
+ };
27
+ var Kalas = class {
28
+ #names;
29
+ #subscribers = /* @__PURE__ */ new Map();
30
+ constructor(names) {
31
+ this.#names = new Set(names);
32
+ Object.defineProperty(this, "events", { value: new Events(this) });
33
+ }
34
+ /**
35
+ * Remove all event subscribers
36
+ */
37
+ clear() {
38
+ this.#subscribers.clear();
39
+ }
40
+ /**
41
+ * Emit an event with parameters
42
+ * @param event Event name
43
+ * @param parameters Event parameters
44
+ */
45
+ emit(event, ...parameters) {
46
+ const subscribers = this.#subscribers.get(event);
47
+ if (subscribers == null) return;
48
+ for (const callback of subscribers) callback(...parameters);
49
+ }
50
+ /**
51
+ * Subscribe to an event with a callback
52
+ * @param event Event name
53
+ * @param callback Callback function
54
+ * @returns Unsubscriber function
55
+ */
56
+ subscribe(event, callback) {
57
+ if (!this.#names.has(event) || typeof callback !== "function") return noop;
58
+ let subscribers = this.#subscribers.get(event);
59
+ if (subscribers == null) {
60
+ subscribers = /* @__PURE__ */ new Set();
61
+ this.#subscribers.set(event, subscribers);
62
+ }
63
+ subscribers.add(callback);
64
+ return () => {
65
+ subscribers?.delete(callback);
66
+ };
67
+ }
68
+ /**
69
+ * Unsubscribe from an event with a callback _(or all callbacks, if no callback is provided)_
70
+ * @param event Event name
71
+ * @param callback Callback function
72
+ */
73
+ unsubscribe(event, callback) {
74
+ if (!this.#names.has(event) || (callback != null ? typeof callback !== "function" : false)) return;
75
+ const subscribers = this.#subscribers.get(event);
76
+ if (subscribers == null) return;
77
+ if (callback == null) subscribers.clear();
78
+ else subscribers.delete(callback);
79
+ if (callback == null || subscribers.size === 0) this.#subscribers.delete(event);
80
+ }
81
+ };
82
+ /**
83
+ * Create a Kalas _(party)_ for named events
84
+ * @param names Event names
85
+ * @returns Kalas instance
86
+ */
87
+ function kalas(names) {
88
+ if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string")) throw new Error(MESSAGE);
89
+ return new Kalas(names);
90
+ }
91
+ const MESSAGE = "Kalas requires an array of event names.";
92
+ //#endregion
93
+ export { kalas };
@@ -33,7 +33,9 @@ function kebabCase(value) {
33
33
  * @returns Lower-cased string
34
34
  */
35
35
  function lowerCase(value) {
36
- return typeof value === "string" ? value.toLocaleLowerCase() : "";
36
+ if (typeof value !== "string") return "";
37
+ memoizedLowerCase ??= memoize((v) => v.toLocaleLowerCase());
38
+ return memoizedLowerCase.run(value);
37
39
  }
38
40
  /**
39
41
  * Convert a string to pascal case _(ThisIsPascalCase)_
@@ -57,8 +59,8 @@ function snakeCase(value) {
57
59
  * @returns Title-cased string
58
60
  */
59
61
  function titleCase(value) {
60
- if (typeof value !== "string") return "";
61
- memoizedTitleCase ??= memoize((v) => v.length < 1 ? capitalize(v) : join(words(v).map(capitalize), " "));
62
+ if (typeof value !== "string" || value.length === 0) return "";
63
+ memoizedTitleCase ??= memoize((v) => v.length < 2 ? capitalize(v) : join(words(v).map(capitalize), " "));
62
64
  return memoizedTitleCase.run(value);
63
65
  }
64
66
  function toCase(type, value, capitalizeAny, capitalizeFirst) {
@@ -98,7 +100,9 @@ function toCaseCallback(value) {
98
100
  * @returns Upper-cased string
99
101
  */
100
102
  function upperCase(value) {
101
- return typeof value === "string" ? value.toLocaleUpperCase() : "";
103
+ if (typeof value !== "string" || value.length === 0) return "";
104
+ memoizedUpperCase ??= memoize((v) => v.toLocaleUpperCase());
105
+ return memoizedUpperCase.run(value);
102
106
  }
103
107
  const CASE_CAMEL = "camel";
104
108
  const CASE_KEBAB = "kebab";
@@ -119,6 +123,8 @@ const delimiters = {
119
123
  [CASE_SNAKE]: DELIMITER_UNDERSCORE
120
124
  };
121
125
  let memoizedCapitalize;
126
+ let memoizedLowerCase;
122
127
  let memoizedTitleCase;
128
+ let memoizedUpperCase;
123
129
  //#endregion
124
130
  export { camelCase, capitalize, kebabCase, lowerCase, pascalCase, snakeCase, titleCase, upperCase };
@@ -0,0 +1,55 @@
1
+ //#region src/string/normalize.d.ts
2
+ /**
3
+ * Options for normalizing a string
4
+ */
5
+ type NormalizeOptions = {
6
+ /**
7
+ * Remove diacritical marks from the string? _(defaults to `true`)_
8
+ */
9
+ deburr?: boolean;
10
+ /**
11
+ * Convert the string to lower case? _(defaults to `true`)_
12
+ */
13
+ lowerCase?: boolean;
14
+ /**
15
+ * Trim the string? _(defaults to `true`)_
16
+ */
17
+ trim?: boolean;
18
+ };
19
+ /**
20
+ * String normalizer function
21
+ */
22
+ type Normalizer = {
23
+ /**
24
+ * Normalize a string
25
+ * @param value String to normalize
26
+ * @returns Normalized string
27
+ */
28
+ (value: string): string;
29
+ };
30
+ /**
31
+ * Deburr a string, removing diacritical marks
32
+ * @param value String to deburr
33
+ * @returns Deburred string
34
+ */
35
+ declare function deburr(value: string): string;
36
+ /**
37
+ * Initialize a string normalizer
38
+ * @param options Normalization options
39
+ * @returns Normalizer function
40
+ */
41
+ declare function initializeNormalizer(options?: NormalizeOptions): Normalizer;
42
+ /**
43
+ * Normalize a string
44
+ *
45
+ * By default, the string will be trimmed, deburred, and then lowercased
46
+ * @param value String to normalize
47
+ * @param options Normalization options
48
+ * @returns Normalized string
49
+ */
50
+ declare function normalize(value: string, options?: NormalizeOptions): string;
51
+ declare namespace normalize {
52
+ var initialize: typeof initializeNormalizer;
53
+ }
54
+ //#endregion
55
+ export { NormalizeOptions, Normalizer, deburr, initializeNormalizer, normalize };
@@ -0,0 +1,93 @@
1
+ import { isPlainObject } from "../internal/is.mjs";
2
+ import { memoize } from "../function/memoize.mjs";
3
+ import { lowerCase } from "./case.mjs";
4
+ //#region src/string/normalize.ts
5
+ /**
6
+ * Deburr a string, removing diacritical marks
7
+ * @param value String to deburr
8
+ * @returns Deburred string
9
+ */
10
+ function deburr(value) {
11
+ if (typeof value !== "string") return "";
12
+ deburrMemoizer ??= memoize((value) => {
13
+ let deburred = value.normalize(DEBURR_NORMALIZATION).replace(DEBURR_PATTERN_SIMPLE, "");
14
+ deburred = deburred.replace(DEBURR_PATTERN_CHARACTERS, (_, character) => DEBURR_CHARACTERS[character]);
15
+ return deburred;
16
+ });
17
+ return deburrMemoizer.run(value);
18
+ }
19
+ function getNormalizeOptions(input) {
20
+ const options = isPlainObject(input) ? input : {};
21
+ return {
22
+ deburr: options.deburr !== false,
23
+ lowerCase: options.lowerCase !== false,
24
+ trim: options.trim !== false
25
+ };
26
+ }
27
+ /**
28
+ * Initialize a string normalizer
29
+ * @param options Normalization options
30
+ * @returns Normalizer function
31
+ */
32
+ function initializeNormalizer(options) {
33
+ const normalization = getNormalizeOptions(options);
34
+ return (value) => normalizeString(value, normalization);
35
+ }
36
+ /**
37
+ * Normalize a string
38
+ *
39
+ * By default, the string will be trimmed, deburred, and then lowercased
40
+ * @param value String to normalize
41
+ * @param options Normalization options
42
+ * @returns Normalized string
43
+ */
44
+ function normalize(value, options) {
45
+ return normalizeString(value, getNormalizeOptions(options));
46
+ }
47
+ normalize.initialize = initializeNormalizer;
48
+ function normalizeString(value, options) {
49
+ if (typeof value !== "string") return "";
50
+ let result = value;
51
+ if (options.trim) result = result.trim();
52
+ if (options.deburr) result = deburr(result);
53
+ if (options.lowerCase) result = lowerCase(result);
54
+ return result;
55
+ }
56
+ const DEBURR_CHARACTERS = {
57
+ Æ: "AE",
58
+ æ: "ae",
59
+ Ð: "D",
60
+ ð: "d",
61
+ Đ: "D",
62
+ đ: "d",
63
+ Ħ: "H",
64
+ ħ: "h",
65
+ IJ: "IJ",
66
+ ij: "ij",
67
+ İ: "I",
68
+ ı: "i",
69
+ ĸ: "k",
70
+ Ŀ: "L",
71
+ ŀ: "l",
72
+ Ł: "L",
73
+ ł: "l",
74
+ Ŋ: "N",
75
+ ŋ: "n",
76
+ ʼn: "'n",
77
+ Œ: "OE",
78
+ œ: "oe",
79
+ Ø: "O",
80
+ ø: "o",
81
+ ſ: "s",
82
+ ß: "ss",
83
+ Þ: "TH",
84
+ þ: "th",
85
+ Ŧ: "T",
86
+ ŧ: "t"
87
+ };
88
+ const DEBURR_NORMALIZATION = "NFD";
89
+ const DEBURR_PATTERN_CHARACTERS = new RegExp(`(${Object.keys(DEBURR_CHARACTERS).join("|")})`, "g");
90
+ const DEBURR_PATTERN_SIMPLE = /[\u0300-\u036f]/g;
91
+ let deburrMemoizer;
92
+ //#endregion
93
+ export { deburr, initializeNormalizer, normalize };
@@ -1,5 +1,6 @@
1
1
  import { omit } from "./omit.mjs";
2
2
  import { pick } from "./pick.mjs";
3
+ import { Shaken, shake } from "./shake.mjs";
3
4
  import { Smushed, smush } from "./smush.mjs";
4
5
  import { Unsmushed, unsmush } from "./unsmush.mjs";
5
- export { type Smushed, type Unsmushed, omit, pick, smush, unsmush };
6
+ export { type Shaken, type Smushed, type Unsmushed, omit, pick, shake, smush, unsmush };
@@ -1,5 +1,6 @@
1
1
  import { omit } from "./omit.mjs";
2
2
  import { pick } from "./pick.mjs";
3
+ import { shake } from "./shake.mjs";
3
4
  import { smush } from "./smush.mjs";
4
5
  import { unsmush } from "./unsmush.mjs";
5
- export { omit, pick, smush, unsmush };
6
+ export { omit, pick, shake, smush, unsmush };
@@ -5,16 +5,29 @@ import { ArrayOrPlainObject, NestedPartial } from "../models.mjs";
5
5
  * Options for merging values
6
6
  */
7
7
  type MergeOptions = {
8
+ /**
9
+ * Assign values to the first array or object instead of creating a new one?
10
+ */
11
+ assignValues?: boolean;
8
12
  /**
9
13
  * Key _(or key epxressions)_ for values that should be replaced
14
+ *
10
15
  * ```ts
11
16
  * merge([{items: [1, 2, 3]}, {items: [99]}]); // {items: [99]}
12
17
  * ```
13
18
  */
14
19
  replaceableObjects?: string | RegExp | Array<string | RegExp>;
20
+ /**
21
+ * Skip nullable values when merging objects?
22
+ *
23
+ * ```ts
24
+ * merge({a: 1, b: 2}, {b: null, c: 3}, {d: null}); // {a: 1, b: 2, c: 3}
25
+ * ```
26
+ */
15
27
  skipNullableAny?: boolean;
16
28
  /**
17
29
  * Skip nullable values when merging arrays?
30
+ *
18
31
  * ```ts
19
32
  * merge([1, 2, 3], [null, null, 99]); // [1, 2, 99]
20
33
  * ```
@@ -3,12 +3,14 @@ import { join } from "../internal/string.mjs";
3
3
  //#region src/value/merge.ts
4
4
  function getMergeOptions(options) {
5
5
  const actual = {
6
+ assignValues: false,
6
7
  replaceableObjects: void 0,
7
8
  skipNullableAny: false,
8
9
  skipNullableInArrays: false
9
10
  };
10
11
  if (typeof options !== "object" || options == null) return actual;
11
12
  actual.replaceableObjects = getReplaceableObjects(options.replaceableObjects);
13
+ actual.assignValues = options.assignValues === true;
12
14
  actual.skipNullableAny = options.skipNullableAny === true;
13
15
  actual.skipNullableInArrays = options.skipNullableInArrays === true;
14
16
  return actual;
@@ -38,7 +40,7 @@ merge.initialize = initializeMerger;
38
40
  function mergeObjects(values, options, prefix) {
39
41
  const { length } = values;
40
42
  const isArray = values.every(Array.isArray);
41
- const merged = isArray ? [] : {};
43
+ const merged = options.assignValues ? values[0] : isArray ? [] : {};
42
44
  for (let outerIndex = 0; outerIndex < length; outerIndex += 1) {
43
45
  const item = values[outerIndex];
44
46
  const keys = Object.keys(item);
@@ -0,0 +1,7 @@
1
+ import { PlainObject } from "../models.mjs";
2
+
3
+ //#region src/value/shake.d.ts
4
+ type Shaken<Value extends PlainObject> = { [Key in keyof Value]: Value[Key] extends undefined ? never : Value[Key] };
5
+ declare function shake<Value extends PlainObject>(value: Value): Shaken<Value>;
6
+ //#endregion
7
+ export { Shaken, shake };
@@ -0,0 +1,16 @@
1
+ import { isNonPlainObject } from "../internal/is.mjs";
2
+ //#region src/value/shake.ts
3
+ function shake(value) {
4
+ const shaken = {};
5
+ if (isNonPlainObject(value)) return shaken;
6
+ const keys = Object.keys(value);
7
+ const { length } = keys;
8
+ for (let index = 0; index < length; index += 1) {
9
+ const key = keys[index];
10
+ const val = value[key];
11
+ if (val !== void 0) shaken[key] = val;
12
+ }
13
+ return shaken;
14
+ }
15
+ //#endregion
16
+ export { shake };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/atoms",
3
- "version": "0.180.0",
3
+ "version": "0.182.0",
4
4
  "description": "Atomic utilities for making your JavaScript better.",
5
5
  "keywords": [
6
6
  "helper",
@@ -52,7 +52,7 @@ export function exists(array: unknown[], ...parameters: unknown[]): boolean {
52
52
  return Array.isArray(array) ? array.includes(parameters[0]) : false;
53
53
  }
54
54
 
55
- return (findValue(FIND_VALUE_INDEX, array, parameters, false) as number) > -1;
55
+ return findValue(FIND_VALUE_INDEX, array, parameters, false) > -1;
56
56
  }
57
57
 
58
58
  // #endregion
package/src/array/find.ts CHANGED
@@ -52,4 +52,62 @@ export function find(array: unknown[], ...parameters: unknown[]): unknown {
52
52
  return findValue(FIND_VALUE_ITEM, array, parameters, false);
53
53
  }
54
54
 
55
+ find.last = findLast;
56
+
57
+ /**
58
+ * Get the last item matching the given value
59
+ *
60
+ * Available as `findLast` and `find.last`
61
+ * @param array Array to search in
62
+ * @param callback Callback to get an item's value for matching
63
+ * @param value Value to match against
64
+ * @returns Last item that matches the value, or `undefined` if no match is found
65
+ */
66
+ export function findLast<
67
+ Item,
68
+ Callback extends (item: Item, index: number, array: Item[]) => unknown,
69
+ >(array: Item[], callback: Callback, value: ReturnType<Callback>): Item | undefined;
70
+
71
+ /**
72
+ * Get the last item matching the given value by key
73
+ *
74
+ * Available as `findLast` and `find.last`
75
+ * @param array Array to search in
76
+ * @param key Key to get an item's value for matching
77
+ * @param value Value to match against
78
+ * @returns Last item that matches the value, or `undefined` if no match is found
79
+ */
80
+ export function findLast<Item extends PlainObject, ItemKey extends keyof Item>(
81
+ array: Item[],
82
+ key: ItemKey,
83
+ value: Item[ItemKey],
84
+ ): Item | undefined;
85
+
86
+ /**
87
+ * Get the last item matching the filter
88
+ *
89
+ * Available as `findLast` and `find.last`
90
+ * @param array Array to search in
91
+ * @param filter Filter callback to match items
92
+ * @returns Last item that matches the filter, or `undefined` if no match is found
93
+ */
94
+ export function findLast<Item>(
95
+ array: Item[],
96
+ filter: (item: Item, index: number, array: Item[]) => boolean,
97
+ ): Item | undefined;
98
+
99
+ /**
100
+ * Get the last item matching the given value
101
+ *
102
+ * Available as `findLast` and `find.last`
103
+ * @param array Array to search in
104
+ * @param value Value to match against
105
+ * @returns Last item that matches the value, or `undefined` if no match is found
106
+ */
107
+ export function findLast<Item>(array: Item[], value: Item): Item | undefined;
108
+
109
+ export function findLast(array: unknown[], ...parameters: unknown[]): unknown {
110
+ return findValue(FIND_VALUE_ITEM, array, parameters, true);
111
+ }
112
+
55
113
  // #endregion
package/src/array/get.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {isPlainObject} from '../internal/is';
1
+ import {isNonPlainObject} from '../internal/is';
2
2
  import type {NumericalKeys, PlainObject} from '../models';
3
3
 
4
4
  // #region Functions
@@ -50,7 +50,7 @@ export function getArray(value: unknown, indiced?: unknown): unknown[] {
50
50
  return [...value.values()];
51
51
  }
52
52
 
53
- if (!isPlainObject(value)) {
53
+ if (isNonPlainObject(value)) {
54
54
  return [value];
55
55
  }
56
56
 
package/src/index.ts CHANGED
@@ -25,10 +25,13 @@ export * from './internal/value/get';
25
25
  export * from './internal/value/has';
26
26
  export * from './internal/value/set';
27
27
 
28
+ export * from './kalas';
29
+
28
30
  export * from './string/case';
29
31
  export * from './string/fuzzy';
30
32
  export * from './string/index';
31
33
  export * from './string/match';
34
+ export * from './string/normalize';
32
35
  export * from './string/template';
33
36
 
34
37
  export * from './value/clone';
@@ -21,6 +21,20 @@ type Parameters = {
21
21
 
22
22
  // #region Functions
23
23
 
24
+ export function findValue(
25
+ type: Exclude<FindValueType, 'item'>,
26
+ array: unknown[],
27
+ parameters: unknown[],
28
+ reversed: boolean,
29
+ ): number;
30
+
31
+ export function findValue(
32
+ type: Exclude<FindValueType, 'index'>,
33
+ array: unknown[],
34
+ parameters: unknown[],
35
+ reversed: boolean,
36
+ ): unknown;
37
+
24
38
  export function findValue(
25
39
  type: FindValueType,
26
40
  array: unknown[],
@@ -38,11 +52,17 @@ export function findValue(
38
52
  const callbacks = getArrayCallbacks(bool, key);
39
53
 
40
54
  if (callbacks?.bool == null && callbacks?.keyed == null) {
41
- return findIndex ? array.indexOf(value) : array.find(item => Object.is(item, value));
55
+ if (findIndex) {
56
+ return reversed ? array.lastIndexOf(value) : array.indexOf(value);
57
+ }
58
+
59
+ return reversed
60
+ ? array.findLast(item => Object.is(item, value))
61
+ : array.find(item => Object.is(item, value));
42
62
  }
43
63
 
44
64
  if (callbacks.bool != null) {
45
- const index = array.findIndex(callbacks.bool);
65
+ const index = reversed ? array.findLastIndex(callbacks.bool) : array.findIndex(callbacks.bool);
46
66
 
47
67
  return findIndex ? index : array[index];
48
68
  }
@@ -167,9 +187,9 @@ export function getFindParameters(original: unknown[]): Parameters {
167
187
 
168
188
  // #region Variables
169
189
 
170
- export const FIND_VALUE_INDEX: FindValueType = 'index';
190
+ export const FIND_VALUE_INDEX = 'index';
171
191
 
172
- export const FIND_VALUE_ITEM: FindValueType = 'item';
192
+ export const FIND_VALUE_ITEM = 'item';
173
193
 
174
194
  export const FIND_VALUES_ALL: FindValuesType = 'all';
175
195
 
@@ -48,7 +48,65 @@ export function indexOf<Item>(
48
48
  export function indexOf<Item>(array: Item[], item: Item): number;
49
49
 
50
50
  export function indexOf(array: unknown[], ...parameters: unknown[]): number {
51
- return findValue(FIND_VALUE_INDEX, array, parameters, false) as number;
51
+ return findValue(FIND_VALUE_INDEX, array, parameters, false);
52
+ }
53
+
54
+ indexOf.last = lastIndexOf;
55
+
56
+ /**
57
+ * Get the index of the last matching item by callback
58
+ *
59
+ * Available as `lastIndexOf` and `indexOf.last`
60
+ * @param array Array to search in
61
+ * @param callback Callback to get an item's value
62
+ * @param value Value to match against
63
+ * @returns Index of the last matching item, or `-1` if no match is found
64
+ */
65
+ export function lastIndexOf<
66
+ Item,
67
+ Callback extends (item: Item, index: number, array: Item[]) => unknown,
68
+ >(array: Item[], callback: Callback, value: ReturnType<Callback>): number;
69
+
70
+ /**
71
+ * Get the index of the last matching item by key
72
+ *
73
+ * Available as `lastIndexOf` and `indexOf.last`
74
+ * @param array Array to search in
75
+ * @param key Key to match items by
76
+ * @param value Value to match against
77
+ * @returns Index of the last matching item, or `-1` if no match is found
78
+ */
79
+ export function lastIndexOf<Item extends PlainObject, ItemKey extends keyof Item>(
80
+ array: Item[],
81
+ key: ItemKey,
82
+ value: Item[ItemKey],
83
+ ): number;
84
+
85
+ /**
86
+ * Get the index of the last item matching the filter
87
+ *
88
+ * Available as `lastIndexOf` and `indexOf.last`
89
+ * @param array Array to search in
90
+ * @param filter Filter callback to match items
91
+ * @returns Index of the last matching item, or `-1` if no match is found
92
+ */
93
+ export function lastIndexOf<Item>(
94
+ array: Item[],
95
+ filter: (item: Item, index: number, array: Item[]) => boolean,
96
+ ): number;
97
+
98
+ /**
99
+ * Get the index of the last item matching the given item
100
+ *
101
+ * Available as `lastIndexOf` and `indexOf.last`
102
+ * @param array Array to search in
103
+ * @param item Item to match against
104
+ * @returns Index of the last matching item, or `-1` if no match is found
105
+ */
106
+ export function lastIndexOf<Item>(array: Item[], item: Item): number;
107
+
108
+ export function lastIndexOf(array: unknown[], ...parameters: unknown[]): number {
109
+ return findValue(FIND_VALUE_INDEX, array, parameters, true);
52
110
  }
53
111
 
54
112
  // #endregion