@revolugo/common 6.10.7-beta.7 → 6.10.7-beta.9
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.
- package/package.json +1 -2
- package/src/cancellation-policies.ts +1 -1
- package/src/currencies/index.ts +1 -1
- package/src/utils/{math.ts → amount-from-percentage.ts} +0 -10
- package/src/utils/case-transformers.ts +0 -1
- package/src/utils/chunk.ts +7 -0
- package/src/utils/colors.ts +1 -1
- package/src/utils/compact-object.ts +7 -0
- package/src/utils/compact.ts +28 -0
- package/src/utils/compute-margin-rate.ts +13 -0
- package/src/utils/compute-selling-price.ts +12 -0
- package/src/utils/defaults-deep.ts +85 -0
- package/src/utils/delay.ts +5 -0
- package/src/utils/generate-numbers-from-str.ts +15 -0
- package/src/utils/generate-pseudo-random-string.ts +3 -0
- package/src/utils/get-guest-count.ts +1 -2
- package/src/utils/get-random-element-from-array.ts +10 -0
- package/src/utils/get-random-hex-color.ts +5 -0
- package/src/utils/{random.ts → get-random-int.ts} +0 -6
- package/src/utils/index.ts +30 -12
- package/src/utils/key-by.ts +25 -0
- package/src/utils/omit-by.ts +37 -0
- package/src/utils/omit.ts +39 -0
- package/src/utils/pick.ts +12 -0
- package/src/utils/poller.ts +0 -1
- package/src/utils/{strings.ts → prepare-ts-query.ts} +0 -6
- package/src/utils/{promise-tools.ts → promise-timeout.ts} +0 -6
- package/src/utils/random-int.ts +3 -0
- package/src/utils/shuffle-array.ts +13 -0
- package/src/utils/{array-tools.ts → sort-by.ts} +0 -103
- package/src/utils/sum-by.ts +23 -0
- package/src/utils/uniq-with.ts +16 -0
- package/src/utils/validators.ts +0 -1
- package/src/utils/weighted-mean.ts +9 -0
- package/src/utils/numbers.ts +0 -46
- package/src/utils/object-tools.ts +0 -210
- /package/src/utils/{value-tools.ts → is-nil.ts} +0 -0
- /package/src/utils/{children-tools.ts → parse-children.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revolugo/common",
|
|
3
|
-
"version": "6.10.7-beta.
|
|
3
|
+
"version": "6.10.7-beta.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Revolugo common",
|
|
6
6
|
"author": "Revolugo",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"change-case": "5.4.4",
|
|
27
27
|
"dayjs": "1.11.18",
|
|
28
28
|
"ky": "1.11.0",
|
|
29
|
-
"lodash-es": "4.17.21",
|
|
30
29
|
"slugify": "1.6.6",
|
|
31
30
|
"uuid": "13.0.0"
|
|
32
31
|
},
|
package/src/currencies/index.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
export function weightedMean(values: number[], weights: number[]): number {
|
|
2
|
-
const sum = values.reduce(
|
|
3
|
-
(acc: number, val: number, i: number) => acc + val * (weights[i] ?? 0),
|
|
4
|
-
0,
|
|
5
|
-
)
|
|
6
|
-
const sumWeights = weights.reduce((acc: number, val: number) => acc + val, 0)
|
|
7
|
-
|
|
8
|
-
return sum / sumWeights
|
|
9
|
-
}
|
|
10
|
-
|
|
11
1
|
export function amountFromPercentage(
|
|
12
2
|
percentage: number,
|
|
13
3
|
amount: number,
|
package/src/utils/colors.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function compact<T>(
|
|
2
|
+
array: (T | null | undefined)[] | null | undefined,
|
|
3
|
+
): Exclude<T, null | undefined>[] {
|
|
4
|
+
const length = array === null || array === undefined ? 0 : array.length
|
|
5
|
+
let index = -1
|
|
6
|
+
let resIndex = 0
|
|
7
|
+
const result: Exclude<T, null | undefined>[] = []
|
|
8
|
+
|
|
9
|
+
while (index < length) {
|
|
10
|
+
index += 1
|
|
11
|
+
if (array !== null && array !== undefined) {
|
|
12
|
+
const value = array[index]
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
value !== null &&
|
|
16
|
+
value !== undefined &&
|
|
17
|
+
value !== false &&
|
|
18
|
+
value !== 0 &&
|
|
19
|
+
value !== ''
|
|
20
|
+
) {
|
|
21
|
+
result[resIndex] = value as Exclude<T, null | undefined>
|
|
22
|
+
resIndex += 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function computeMarginRate(
|
|
2
|
+
buyingPrice: number | string,
|
|
3
|
+
sellingPrice: number | string,
|
|
4
|
+
): number {
|
|
5
|
+
const buyingPriceNum = Number(buyingPrice)
|
|
6
|
+
const sellingPriceNum = Number(sellingPrice)
|
|
7
|
+
|
|
8
|
+
if (Number.isNaN(sellingPriceNum) || Number.isNaN(buyingPriceNum)) {
|
|
9
|
+
throw new TypeError('sellingPrice or buyingPrice is NaN')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return Number((1 - buyingPriceNum / sellingPriceNum).toPrecision(10))
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function computeSellingPrice(
|
|
2
|
+
buyingPrice: number | string | null = 0,
|
|
3
|
+
rate: number | string | null = 0,
|
|
4
|
+
): number {
|
|
5
|
+
if (Number.isNaN(Number(buyingPrice)) || Number.isNaN(Number(rate))) {
|
|
6
|
+
throw new TypeError('price or rate is NaN')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const sellingPrice = Number(buyingPrice) / (1 - Number(rate))
|
|
10
|
+
|
|
11
|
+
return Math.ceil(Math.floor(sellingPrice * 100) / 100)
|
|
12
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop-in native TypeScript replacement for `lodash-es` `defaultsDeep`.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour matched (carefully):
|
|
5
|
+
* - Mutates and returns the first argument (target).
|
|
6
|
+
* - Assigns own and *inherited* enumerable **string-keyed** properties from sources
|
|
7
|
+
* to the target only when the target's property is `undefined`.
|
|
8
|
+
* - Recursively assigns (deep) when both target and source values are objects/arrays.
|
|
9
|
+
* - Does not copy symbol-keyed properties (matches the "string keyed" behaviour).
|
|
10
|
+
* - Preserves `null` (i.e. `null` is NOT considered `undefined` so it won't be overwritten).
|
|
11
|
+
* - Avoids infinite recursion for circular source structures.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
15
|
+
type AnyObject = Record<string, any>
|
|
16
|
+
|
|
17
|
+
function isObject(value: any): value is AnyObject {
|
|
18
|
+
return value !== null && typeof value === 'object'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defaultsDeep<T>(
|
|
22
|
+
object: T,
|
|
23
|
+
...sources: any[]
|
|
24
|
+
): T extends AnyObject ? T : any {
|
|
25
|
+
// Ensure we always return an object reference we can mutate (lodash creates/uses {} when target is falsy)
|
|
26
|
+
const target: AnyObject = (
|
|
27
|
+
object === null || object === undefined ? {} : object
|
|
28
|
+
) as AnyObject
|
|
29
|
+
|
|
30
|
+
// WeakMap to track already-cloned source objects so circular refs won't blow the stack
|
|
31
|
+
const seen = new WeakMap<object, object>()
|
|
32
|
+
|
|
33
|
+
function applyDefaults(dst: AnyObject, src: any) {
|
|
34
|
+
// Only iterate string-keyed enumerable properties (own + inherited) => for...in
|
|
35
|
+
for (const key in src) {
|
|
36
|
+
if (Object.hasOwn(src, key)) {
|
|
37
|
+
const srcVal = src[key]
|
|
38
|
+
const dstVal = dst[key]
|
|
39
|
+
|
|
40
|
+
// only apply when destination is strictly `undefined` (lodash semantics)
|
|
41
|
+
if (dstVal === undefined) {
|
|
42
|
+
// eslint-disable-next-line max-depth
|
|
43
|
+
if (isObject(srcVal)) {
|
|
44
|
+
// If we've already cloned this source object (circular), reuse it
|
|
45
|
+
// eslint-disable-next-line max-depth
|
|
46
|
+
if (seen.has(srcVal)) {
|
|
47
|
+
dst[key] = seen.get(srcVal)
|
|
48
|
+
} else {
|
|
49
|
+
// Prepare new container (array vs object)
|
|
50
|
+
const created = Array.isArray(srcVal)
|
|
51
|
+
? ([] as unknown[])
|
|
52
|
+
: ({} as AnyObject)
|
|
53
|
+
// remember mapping to handle circular refs
|
|
54
|
+
seen.set(srcVal, created)
|
|
55
|
+
|
|
56
|
+
// Recursively fill created from srcVal
|
|
57
|
+
applyDefaults(created, srcVal)
|
|
58
|
+
|
|
59
|
+
// assign the created clone as the default
|
|
60
|
+
dst[key] = created
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// Primitive or function: assign directly
|
|
64
|
+
dst[key] = srcVal
|
|
65
|
+
}
|
|
66
|
+
} else if (isObject(dstVal) && isObject(srcVal)) {
|
|
67
|
+
// destination already has a value (not undefined) and both are objects => recurse
|
|
68
|
+
// (do not overwrite existing non-undefined values)
|
|
69
|
+
// Note: do not treat arrays specially here; Array.isArray check only used when creating new value
|
|
70
|
+
applyDefaults(dstVal, srcVal)
|
|
71
|
+
}
|
|
72
|
+
// else: destination has a non-undefined primitive or non-object - skip (do not overwrite)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const src of sources) {
|
|
78
|
+
if (src !== null && src !== undefined) {
|
|
79
|
+
// If source itself has been seen (unlikely across top-level sources), we still traverse it
|
|
80
|
+
applyDefaults(target, src)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return target as any
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function generateNumbersFromStr(str: string): [number, number] {
|
|
2
|
+
let hash = 5381
|
|
3
|
+
for (let i = 0; i < str.length; i++) {
|
|
4
|
+
// eslint-disable-next-line no-bitwise
|
|
5
|
+
hash = (hash << 5) + hash + str.charCodeAt(i)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Generate two numbers using the hash
|
|
9
|
+
// eslint-disable-next-line no-bitwise
|
|
10
|
+
const num1 = (hash & 0xff_ff) / 0xff_ff
|
|
11
|
+
// eslint-disable-next-line no-bitwise
|
|
12
|
+
const num2 = ((hash >> 16) & 0xff_ff) / 0xff_ff
|
|
13
|
+
|
|
14
|
+
return [num1, num2]
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getRandomElementFromArray<T>(array: readonly T[]): T {
|
|
2
|
+
if (array.length === 0) {
|
|
3
|
+
throw new Error('Cannot get random element from empty array')
|
|
4
|
+
}
|
|
5
|
+
const element = array[Math.floor(Math.random() * array.length)]
|
|
6
|
+
if (element === undefined) {
|
|
7
|
+
throw new Error('Array element is undefined')
|
|
8
|
+
}
|
|
9
|
+
return element
|
|
10
|
+
}
|
|
@@ -4,9 +4,3 @@ export function getRandomInt(min: number, max: number): number {
|
|
|
4
4
|
|
|
5
5
|
return Math.floor(Math.random() * (roundedMax - roundedMin + 1)) + roundedMin
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
export function getRandomHexColor(): string {
|
|
9
|
-
const hex = Math.floor(Math.random() * 0xffffff).toString(16)
|
|
10
|
-
|
|
11
|
-
return hex.padStart(6, '0').toUpperCase()
|
|
12
|
-
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,32 +1,50 @@
|
|
|
1
1
|
export * from './add-classes.ts'
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './amount-from-percentage.ts'
|
|
3
3
|
export * from './case-transformers.ts'
|
|
4
|
+
export * from './chunk.ts'
|
|
4
5
|
export * from './colors.ts'
|
|
6
|
+
export * from './compact-object.ts'
|
|
7
|
+
export * from './compact.ts'
|
|
8
|
+
export * from './compute-margin-rate.ts'
|
|
9
|
+
export * from './compute-selling-price.ts'
|
|
10
|
+
export * from './create-composite-key.ts'
|
|
5
11
|
export * from './currency.ts'
|
|
6
12
|
export * from './dates.ts'
|
|
7
13
|
export * from './dayjs.ts'
|
|
8
14
|
export * from './debounce.ts'
|
|
15
|
+
export * from './defaults-deep.ts'
|
|
16
|
+
export * from './delay.ts'
|
|
9
17
|
export * from './find-unique-keys.ts'
|
|
18
|
+
export * from './generate-numbers-from-str.ts'
|
|
19
|
+
export * from './generate-pseudo-random-string.ts'
|
|
10
20
|
export * from './get-guest-count.ts'
|
|
21
|
+
export * from './get-random-element-from-array.ts'
|
|
22
|
+
export * from './get-random-hex-color.ts'
|
|
23
|
+
export * from './get-random-int.ts'
|
|
11
24
|
export * from './group-by.ts'
|
|
25
|
+
export * from './images.ts'
|
|
12
26
|
export * from './is-empty.ts'
|
|
13
27
|
export * from './is-equal.ts'
|
|
28
|
+
export * from './is-nil.ts'
|
|
29
|
+
export * from './key-by.ts'
|
|
14
30
|
export * from './lang-default-fallbacks.ts'
|
|
15
|
-
export * from './math.ts'
|
|
16
31
|
export * from './merge.ts'
|
|
17
|
-
export * from './
|
|
18
|
-
export * from './
|
|
32
|
+
export * from './omit-by.ts'
|
|
33
|
+
export * from './omit.ts'
|
|
34
|
+
export * from './parse-children.ts'
|
|
35
|
+
export * from './pick.ts'
|
|
19
36
|
export * from './poller.ts'
|
|
20
|
-
export * from './
|
|
21
|
-
export * from './
|
|
37
|
+
export * from './prepare-ts-query.ts'
|
|
38
|
+
export * from './promise-timeout.ts'
|
|
39
|
+
export * from './random-int.ts'
|
|
22
40
|
export * from './range.ts'
|
|
23
|
-
export * from './
|
|
41
|
+
export * from './shake.ts'
|
|
42
|
+
export * from './shuffle-array.ts'
|
|
43
|
+
export * from './sort-by.ts'
|
|
44
|
+
export * from './sum-by.ts'
|
|
24
45
|
export * from './sum.ts'
|
|
25
46
|
export * from './to-boolean.ts'
|
|
26
47
|
export * from './uniq-by.ts'
|
|
48
|
+
export * from './uniq-with.ts'
|
|
27
49
|
export * from './validators.ts'
|
|
28
|
-
export * from './
|
|
29
|
-
export * from './create-composite-key.ts'
|
|
30
|
-
export * from './children-tools.ts'
|
|
31
|
-
export * from './shake.ts'
|
|
32
|
-
export * from './images.ts'
|
|
50
|
+
export * from './weighted-mean.ts'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an object composed of keys generated from the results of running each element of collection through iteratee.
|
|
3
|
+
* The corresponding value of each key is the last element responsible for generating the key.
|
|
4
|
+
*
|
|
5
|
+
* @param collection - The collection to iterate over
|
|
6
|
+
* @param iteratee - The iteratee to transform keys. Can be a function or a property name
|
|
7
|
+
* @returns Returns the composed aggregate object
|
|
8
|
+
*/
|
|
9
|
+
export function keyBy<T>(
|
|
10
|
+
collection: T[] | null | undefined,
|
|
11
|
+
iteratee: ((value: T) => string | number) | keyof T,
|
|
12
|
+
): Record<string, T> {
|
|
13
|
+
return (
|
|
14
|
+
collection?.reduce<Record<string, T>>((acc, item) => {
|
|
15
|
+
if (item === undefined || item === null) {
|
|
16
|
+
return acc
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const key =
|
|
20
|
+
typeof iteratee === 'function' ? iteratee(item) : String(item[iteratee])
|
|
21
|
+
acc[key] = item
|
|
22
|
+
return acc
|
|
23
|
+
}, {}) || {}
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an object composed of the own enumerable properties of object that predicate doesn't return truthy for.
|
|
3
|
+
* The predicate is invoked with two arguments: (value, key).
|
|
4
|
+
* If predicate is a string, it omits the property with that key name.
|
|
5
|
+
*
|
|
6
|
+
* @param object - The source object
|
|
7
|
+
* @param predicate - The function invoked per property or a string key to omit
|
|
8
|
+
* @returns Returns the new object
|
|
9
|
+
*/
|
|
10
|
+
export function omitBy<T extends object>(
|
|
11
|
+
object: T,
|
|
12
|
+
predicate: ((value: T[keyof T], key: keyof T) => boolean) | string,
|
|
13
|
+
): Partial<T> {
|
|
14
|
+
const result = {} as Partial<T>
|
|
15
|
+
|
|
16
|
+
for (const key in object) {
|
|
17
|
+
if (Object.hasOwn(object, key)) {
|
|
18
|
+
const value = object[key]
|
|
19
|
+
|
|
20
|
+
// If predicate is a string, check if key matches
|
|
21
|
+
if (typeof predicate === 'string') {
|
|
22
|
+
// eslint-disable-next-line max-depth
|
|
23
|
+
if (key !== predicate) {
|
|
24
|
+
result[key] = value
|
|
25
|
+
}
|
|
26
|
+
} else if (typeof predicate === 'function') {
|
|
27
|
+
// If predicate is a function, use it
|
|
28
|
+
// eslint-disable-next-line max-depth
|
|
29
|
+
if (!predicate(value, key)) {
|
|
30
|
+
result[key] = value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a shallow clone of `object` excluding the given own enumerable keys.
|
|
3
|
+
* Accepts a single key or an array of keys. Symbol keys are supported.
|
|
4
|
+
* If `object` is nullish, returns an empty object.
|
|
5
|
+
*/
|
|
6
|
+
export function omit<T extends object, K extends keyof T>(
|
|
7
|
+
object: T | null | undefined,
|
|
8
|
+
keys: readonly K[] | K,
|
|
9
|
+
): Omit<T, K> {
|
|
10
|
+
if (object === null || object === undefined) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
return {} as any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const keysArray = (
|
|
16
|
+
Array.isArray(keys) ? keys : [keys]
|
|
17
|
+
) as readonly (keyof T)[]
|
|
18
|
+
const keysSet = new Set<keyof T>(keysArray as (keyof T)[])
|
|
19
|
+
|
|
20
|
+
const result: Partial<T> = {}
|
|
21
|
+
|
|
22
|
+
// Copy string/number keys
|
|
23
|
+
for (const key in object) {
|
|
24
|
+
if (Object.hasOwn(object, key) && !keysSet.has(key as keyof T)) {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
;(result as any)[key] = object[key as keyof T]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Copy symbol keys
|
|
31
|
+
for (const sym of Object.getOwnPropertySymbols(object)) {
|
|
32
|
+
if (!keysSet.has(sym as keyof T)) {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
;(result as any)[sym as unknown as keyof T] = (object as any)[sym]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result as Omit<T, K>
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function pick<T extends object, K extends keyof T>(
|
|
2
|
+
obj: T,
|
|
3
|
+
keys: readonly K[],
|
|
4
|
+
): Pick<T, K> {
|
|
5
|
+
const result = {} as Pick<T, K>
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (Object.hasOwn(obj, key)) {
|
|
8
|
+
result[key] = obj[key]
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return result
|
|
12
|
+
}
|
package/src/utils/poller.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export function generatePseudoRandomString(length: number): string {
|
|
2
|
-
return Array.from({ length }, () => Math.random().toString(36)[2]).join('')
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
/* @__PURE__ */
|
|
6
1
|
const TS_QUERY_SPECIAL_CHARS = new Set([
|
|
7
2
|
'&',
|
|
8
3
|
'|',
|
|
@@ -15,7 +10,6 @@ const TS_QUERY_SPECIAL_CHARS = new Set([
|
|
|
15
10
|
'@',
|
|
16
11
|
])
|
|
17
12
|
|
|
18
|
-
/* @__PURE__ */
|
|
19
13
|
const REGEX_SPECIAL_CHARS = new Set(['|', '(', ')'])
|
|
20
14
|
|
|
21
15
|
export function prepareTsQuery(query: string): string | null {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function shuffleArray<T>(array: T[]): T[] {
|
|
2
|
+
const shuffled = [...array]
|
|
3
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
4
|
+
const j = Math.floor(Math.random() * (i + 1))
|
|
5
|
+
const temp = shuffled[i]
|
|
6
|
+
const swap = shuffled[j]
|
|
7
|
+
if (temp !== undefined && swap !== undefined) {
|
|
8
|
+
shuffled[i] = swap
|
|
9
|
+
shuffled[j] = temp
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return shuffled
|
|
13
|
+
}
|
|
@@ -1,106 +1,3 @@
|
|
|
1
|
-
export function chunk<T>(array: T[], size: number): T[][] {
|
|
2
|
-
const chunks: T[][] = []
|
|
3
|
-
for (let i = 0; i < array.length; i += size) {
|
|
4
|
-
chunks.push(array.slice(i, i + size))
|
|
5
|
-
}
|
|
6
|
-
return chunks
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getRandomElementFromArray<T>(array: readonly T[]): T {
|
|
10
|
-
if (array.length === 0) {
|
|
11
|
-
throw new Error('Cannot get random element from empty array')
|
|
12
|
-
}
|
|
13
|
-
const element = array[Math.floor(Math.random() * array.length)]
|
|
14
|
-
if (element === undefined) {
|
|
15
|
-
throw new Error('Array element is undefined')
|
|
16
|
-
}
|
|
17
|
-
return element
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function shuffleArray<T>(array: T[]): T[] {
|
|
21
|
-
const shuffled = [...array]
|
|
22
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
23
|
-
const j = Math.floor(Math.random() * (i + 1))
|
|
24
|
-
const temp = shuffled[i]
|
|
25
|
-
const swap = shuffled[j]
|
|
26
|
-
if (temp !== undefined && swap !== undefined) {
|
|
27
|
-
shuffled[i] = swap
|
|
28
|
-
shuffled[j] = temp
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return shuffled
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type Comparator<T> = (a: T, b: T) => boolean
|
|
35
|
-
|
|
36
|
-
export function uniqWith<T>(array: T[], comparator: Comparator<T>): T[] {
|
|
37
|
-
if (!Array.isArray(array) || typeof comparator !== 'function') {
|
|
38
|
-
throw new TypeError(
|
|
39
|
-
'First argument must be an array and second argument must be a function',
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return array.reduce((unique: T[], current: T) => {
|
|
44
|
-
// Check if current item matches any existing item in unique array based on comparator
|
|
45
|
-
const hasDuplicate = unique.some(item => comparator(item, current))
|
|
46
|
-
// If no duplicate found, add current item to unique array
|
|
47
|
-
return hasDuplicate ? unique : [...unique, current]
|
|
48
|
-
}, [])
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function compact<T>(
|
|
52
|
-
array: (T | null | undefined)[] | null | undefined,
|
|
53
|
-
): Exclude<T, null | undefined>[] {
|
|
54
|
-
const length = array === null || array === undefined ? 0 : array.length
|
|
55
|
-
let index = -1
|
|
56
|
-
let resIndex = 0
|
|
57
|
-
const result: Exclude<T, null | undefined>[] = []
|
|
58
|
-
|
|
59
|
-
while (index < length) {
|
|
60
|
-
index += 1
|
|
61
|
-
if (array !== null && array !== undefined) {
|
|
62
|
-
const value = array[index]
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
value !== null &&
|
|
66
|
-
value !== undefined &&
|
|
67
|
-
value !== false &&
|
|
68
|
-
value !== 0 &&
|
|
69
|
-
value !== ''
|
|
70
|
-
) {
|
|
71
|
-
result[resIndex] = value as Exclude<T, null | undefined>
|
|
72
|
-
resIndex += 1
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return result
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function sumBy<T>(
|
|
81
|
-
array: T[] | null | undefined,
|
|
82
|
-
iteratee: ((value: T) => number) | keyof T,
|
|
83
|
-
): number {
|
|
84
|
-
if (!array || !array.length) {
|
|
85
|
-
return 0
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const getValue =
|
|
89
|
-
typeof iteratee === 'function'
|
|
90
|
-
? iteratee
|
|
91
|
-
: (item: T) => item[iteratee] as unknown as number
|
|
92
|
-
|
|
93
|
-
let result: number | undefined = undefined
|
|
94
|
-
for (const item of array) {
|
|
95
|
-
const current = getValue(item)
|
|
96
|
-
if (current !== undefined) {
|
|
97
|
-
result = result === undefined ? current : result + current
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return result ?? 0
|
|
102
|
-
}
|
|
103
|
-
|
|
104
1
|
type Iteratee<T> = ((item: T) => unknown) | keyof T
|
|
105
2
|
type SortOrder = 'asc' | 'desc'
|
|
106
3
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function sumBy<T>(
|
|
2
|
+
array: T[] | null | undefined,
|
|
3
|
+
iteratee: ((value: T) => number) | keyof T,
|
|
4
|
+
): number {
|
|
5
|
+
if (!array || !array.length) {
|
|
6
|
+
return 0
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const getValue =
|
|
10
|
+
typeof iteratee === 'function'
|
|
11
|
+
? iteratee
|
|
12
|
+
: (item: T) => item[iteratee] as unknown as number
|
|
13
|
+
|
|
14
|
+
let result: number | undefined = undefined
|
|
15
|
+
for (const item of array) {
|
|
16
|
+
const current = getValue(item)
|
|
17
|
+
if (current !== undefined) {
|
|
18
|
+
result = result === undefined ? current : result + current
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return result ?? 0
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type Comparator<T> = (a: T, b: T) => boolean
|
|
2
|
+
|
|
3
|
+
export function uniqWith<T>(array: T[], comparator: Comparator<T>): T[] {
|
|
4
|
+
if (!Array.isArray(array) || typeof comparator !== 'function') {
|
|
5
|
+
throw new TypeError(
|
|
6
|
+
'First argument must be an array and second argument must be a function',
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return array.reduce((unique: T[], current: T) => {
|
|
11
|
+
// Check if current item matches any existing item in unique array based on comparator
|
|
12
|
+
const hasDuplicate = unique.some(item => comparator(item, current))
|
|
13
|
+
// If no duplicate found, add current item to unique array
|
|
14
|
+
return hasDuplicate ? unique : [...unique, current]
|
|
15
|
+
}, [])
|
|
16
|
+
}
|
package/src/utils/validators.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function weightedMean(values: number[], weights: number[]): number {
|
|
2
|
+
const sum = values.reduce(
|
|
3
|
+
(acc: number, val: number, i: number) => acc + val * (weights[i] ?? 0),
|
|
4
|
+
0,
|
|
5
|
+
)
|
|
6
|
+
const sumWeights = weights.reduce((acc: number, val: number) => acc + val, 0)
|
|
7
|
+
|
|
8
|
+
return sum / sumWeights
|
|
9
|
+
}
|
package/src/utils/numbers.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export function generateNumbersFromStr(str: string): [number, number] {
|
|
2
|
-
let hash = 5381
|
|
3
|
-
for (let i = 0; i < str.length; i++) {
|
|
4
|
-
// eslint-disable-next-line no-bitwise
|
|
5
|
-
hash = (hash << 5) + hash + str.charCodeAt(i)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// Generate two numbers using the hash
|
|
9
|
-
// eslint-disable-next-line no-bitwise
|
|
10
|
-
const num1 = (hash & 0xff_ff) / 0xff_ff
|
|
11
|
-
// eslint-disable-next-line no-bitwise
|
|
12
|
-
const num2 = ((hash >> 16) & 0xff_ff) / 0xff_ff
|
|
13
|
-
|
|
14
|
-
return [num1, num2]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function randomInt(min: number, max: number): number {
|
|
18
|
-
return Math.floor(Math.random() * (max - min + 1) + min)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function computeSellingPrice(
|
|
22
|
-
buyingPrice: number | string | null = 0,
|
|
23
|
-
rate: number | string | null = 0,
|
|
24
|
-
): number {
|
|
25
|
-
if (Number.isNaN(Number(buyingPrice)) || Number.isNaN(Number(rate))) {
|
|
26
|
-
throw new TypeError('price or rate is NaN')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const sellingPrice = Number(buyingPrice) / (1 - Number(rate))
|
|
30
|
-
|
|
31
|
-
return Math.ceil(Math.floor(sellingPrice * 100) / 100)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function computeMarginRate(
|
|
35
|
-
buyingPrice: number | string,
|
|
36
|
-
sellingPrice: number | string,
|
|
37
|
-
): number {
|
|
38
|
-
const buyingPriceNum = Number(buyingPrice)
|
|
39
|
-
const sellingPriceNum = Number(sellingPrice)
|
|
40
|
-
|
|
41
|
-
if (Number.isNaN(sellingPriceNum) || Number.isNaN(buyingPriceNum)) {
|
|
42
|
-
throw new TypeError('sellingPrice or buyingPrice is NaN')
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return Number((1 - buyingPriceNum / sellingPriceNum).toPrecision(10))
|
|
46
|
-
}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { isNil } from './value-tools.ts'
|
|
2
|
-
|
|
3
|
-
export function pick<T extends object, K extends keyof T>(
|
|
4
|
-
obj: T,
|
|
5
|
-
keys: readonly K[],
|
|
6
|
-
): Pick<T, K> {
|
|
7
|
-
const result = {} as Pick<T, K>
|
|
8
|
-
for (const key of keys) {
|
|
9
|
-
if (Object.hasOwn(obj, key)) {
|
|
10
|
-
result[key] = obj[key]
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return result
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function compactObject<T extends object>(obj: T): Partial<T> {
|
|
17
|
-
return Object.fromEntries(
|
|
18
|
-
Object.entries(obj).filter(([, value]) => !isNil(value)),
|
|
19
|
-
) as Partial<T>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Creates an object composed of keys generated from the results of running each element of collection through iteratee.
|
|
24
|
-
* The corresponding value of each key is the last element responsible for generating the key.
|
|
25
|
-
*
|
|
26
|
-
* @param collection - The collection to iterate over
|
|
27
|
-
* @param iteratee - The iteratee to transform keys. Can be a function or a property name
|
|
28
|
-
* @returns Returns the composed aggregate object
|
|
29
|
-
*/
|
|
30
|
-
export function keyBy<T>(
|
|
31
|
-
collection: T[] | null | undefined,
|
|
32
|
-
iteratee: ((value: T) => string | number) | keyof T,
|
|
33
|
-
): Record<string, T> {
|
|
34
|
-
return (
|
|
35
|
-
collection?.reduce<Record<string, T>>((acc, item) => {
|
|
36
|
-
if (item === undefined || item === null) {
|
|
37
|
-
return acc
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const key =
|
|
41
|
-
typeof iteratee === 'function' ? iteratee(item) : String(item[iteratee])
|
|
42
|
-
acc[key] = item
|
|
43
|
-
return acc
|
|
44
|
-
}, {}) || {}
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Creates an object composed of the own enumerable properties of object that predicate doesn't return truthy for.
|
|
50
|
-
* The predicate is invoked with two arguments: (value, key).
|
|
51
|
-
* If predicate is a string, it omits the property with that key name.
|
|
52
|
-
*
|
|
53
|
-
* @param object - The source object
|
|
54
|
-
* @param predicate - The function invoked per property or a string key to omit
|
|
55
|
-
* @returns Returns the new object
|
|
56
|
-
*/
|
|
57
|
-
export function omitBy<T extends object>(
|
|
58
|
-
object: T,
|
|
59
|
-
predicate: ((value: T[keyof T], key: keyof T) => boolean) | string,
|
|
60
|
-
): Partial<T> {
|
|
61
|
-
const result = {} as Partial<T>
|
|
62
|
-
|
|
63
|
-
for (const key in object) {
|
|
64
|
-
if (Object.hasOwn(object, key)) {
|
|
65
|
-
const value = object[key]
|
|
66
|
-
|
|
67
|
-
// If predicate is a string, check if key matches
|
|
68
|
-
if (typeof predicate === 'string') {
|
|
69
|
-
// eslint-disable-next-line max-depth
|
|
70
|
-
if (key !== predicate) {
|
|
71
|
-
result[key] = value
|
|
72
|
-
}
|
|
73
|
-
} else if (typeof predicate === 'function') {
|
|
74
|
-
// If predicate is a function, use it
|
|
75
|
-
// eslint-disable-next-line max-depth
|
|
76
|
-
if (!predicate(value, key)) {
|
|
77
|
-
result[key] = value
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return result
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Creates a shallow clone of `object` excluding the given own enumerable keys.
|
|
88
|
-
* Accepts a single key or an array of keys. Symbol keys are supported.
|
|
89
|
-
* If `object` is nullish, returns an empty object.
|
|
90
|
-
*/
|
|
91
|
-
export function omit<T extends object, K extends keyof T>(
|
|
92
|
-
object: T | null | undefined,
|
|
93
|
-
keys: readonly K[] | K,
|
|
94
|
-
): Omit<T, K> {
|
|
95
|
-
if (object === null || object === undefined) {
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
-
return {} as any
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const keysArray = (
|
|
101
|
-
Array.isArray(keys) ? keys : [keys]
|
|
102
|
-
) as readonly (keyof T)[]
|
|
103
|
-
const keysSet = new Set<keyof T>(keysArray as (keyof T)[])
|
|
104
|
-
|
|
105
|
-
const result: Partial<T> = {}
|
|
106
|
-
|
|
107
|
-
// Copy string/number keys
|
|
108
|
-
for (const key in object) {
|
|
109
|
-
if (Object.hasOwn(object, key) && !keysSet.has(key as keyof T)) {
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
-
;(result as any)[key] = object[key as keyof T]
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Copy symbol keys
|
|
116
|
-
for (const sym of Object.getOwnPropertySymbols(object)) {
|
|
117
|
-
if (!keysSet.has(sym as keyof T)) {
|
|
118
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
-
;(result as any)[sym as unknown as keyof T] = (object as any)[sym]
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return result as Omit<T, K>
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Drop-in native TypeScript replacement for `lodash-es` `defaultsDeep`.
|
|
128
|
-
*
|
|
129
|
-
* Behaviour matched (carefully):
|
|
130
|
-
* - Mutates and returns the first argument (target).
|
|
131
|
-
* - Assigns own and *inherited* enumerable **string-keyed** properties from sources
|
|
132
|
-
* to the target only when the target's property is `undefined`.
|
|
133
|
-
* - Recursively assigns (deep) when both target and source values are objects/arrays.
|
|
134
|
-
* - Does not copy symbol-keyed properties (matches the "string keyed" behaviour).
|
|
135
|
-
* - Preserves `null` (i.e. `null` is NOT considered `undefined` so it won't be overwritten).
|
|
136
|
-
* - Avoids infinite recursion for circular source structures.
|
|
137
|
-
*/
|
|
138
|
-
|
|
139
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
140
|
-
type AnyObject = Record<string, any>
|
|
141
|
-
|
|
142
|
-
function isObject(value: any): value is AnyObject {
|
|
143
|
-
return value !== null && typeof value === 'object'
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function defaultsDeep<T>(
|
|
147
|
-
object: T,
|
|
148
|
-
...sources: any[]
|
|
149
|
-
): T extends AnyObject ? T : any {
|
|
150
|
-
// Ensure we always return an object reference we can mutate (lodash creates/uses {} when target is falsy)
|
|
151
|
-
const target: AnyObject = (
|
|
152
|
-
object === null || object === undefined ? {} : object
|
|
153
|
-
) as AnyObject
|
|
154
|
-
|
|
155
|
-
// WeakMap to track already-cloned source objects so circular refs won't blow the stack
|
|
156
|
-
const seen = new WeakMap<object, object>()
|
|
157
|
-
|
|
158
|
-
function applyDefaults(dst: AnyObject, src: any) {
|
|
159
|
-
// Only iterate string-keyed enumerable properties (own + inherited) => for...in
|
|
160
|
-
for (const key in src) {
|
|
161
|
-
if (Object.hasOwn(src, key)) {
|
|
162
|
-
const srcVal = src[key]
|
|
163
|
-
const dstVal = dst[key]
|
|
164
|
-
|
|
165
|
-
// only apply when destination is strictly `undefined` (lodash semantics)
|
|
166
|
-
if (dstVal === undefined) {
|
|
167
|
-
// eslint-disable-next-line max-depth
|
|
168
|
-
if (isObject(srcVal)) {
|
|
169
|
-
// If we've already cloned this source object (circular), reuse it
|
|
170
|
-
// eslint-disable-next-line max-depth
|
|
171
|
-
if (seen.has(srcVal)) {
|
|
172
|
-
dst[key] = seen.get(srcVal)
|
|
173
|
-
} else {
|
|
174
|
-
// Prepare new container (array vs object)
|
|
175
|
-
const created = Array.isArray(srcVal)
|
|
176
|
-
? ([] as unknown[])
|
|
177
|
-
: ({} as AnyObject)
|
|
178
|
-
// remember mapping to handle circular refs
|
|
179
|
-
seen.set(srcVal, created)
|
|
180
|
-
|
|
181
|
-
// Recursively fill created from srcVal
|
|
182
|
-
applyDefaults(created, srcVal)
|
|
183
|
-
|
|
184
|
-
// assign the created clone as the default
|
|
185
|
-
dst[key] = created
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
// Primitive or function: assign directly
|
|
189
|
-
dst[key] = srcVal
|
|
190
|
-
}
|
|
191
|
-
} else if (isObject(dstVal) && isObject(srcVal)) {
|
|
192
|
-
// destination already has a value (not undefined) and both are objects => recurse
|
|
193
|
-
// (do not overwrite existing non-undefined values)
|
|
194
|
-
// Note: do not treat arrays specially here; Array.isArray check only used when creating new value
|
|
195
|
-
applyDefaults(dstVal, srcVal)
|
|
196
|
-
}
|
|
197
|
-
// else: destination has a non-undefined primitive or non-object - skip (do not overwrite)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
for (const src of sources) {
|
|
203
|
-
if (src !== null && src !== undefined) {
|
|
204
|
-
// If source itself has been seen (unlikely across top-level sources), we still traverse it
|
|
205
|
-
applyDefaults(target, src)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return target as any
|
|
210
|
-
}
|
|
File without changes
|
|
File without changes
|