Document more utils

This commit is contained in:
Aria Minaei 2022-11-24 14:09:02 +01:00
parent 3f5b902739
commit 5ee13a20a0
4 changed files with 94 additions and 85 deletions

View file

@ -2,11 +2,24 @@ import type {Pointer} from '@theatre/dataverse'
import {getPointerParts, pointer} from '@theatre/dataverse' import {getPointerParts, pointer} from '@theatre/dataverse'
import lodashSet from 'lodash-es/set' import lodashSet from 'lodash-es/set'
/**
* Like `lodash.set`, but type-safe, as it uses `Pointer` instead of string/array.
*
* `getPointer` is a function that takes a pointer to `obj`, and returns a pointer
* to the path that you want to set. This API looks funny but it is actually convenient
* to mutate values type-safe, as you can see in the example below:
*
* ```ts
* mutableSetDeep({a: {b: 1}}, (p) => p.a.b, 2) // {a: {b: 2}}
* ```
*/
export default function mutableSetDeep<O extends {}, T>( export default function mutableSetDeep<O extends {}, T>(
obj: O, obj: O,
getPath: (p: Pointer<O>) => Pointer<T>, getPointer: (p: Pointer<O>) => Pointer<T>,
val: T, val: T,
) { ) {
const path = getPointerParts(getPath(pointer<O>({root: {}, path: []}))).path const rootPointer = pointer<O>({root: {}, path: []})
lodashSet(obj, path, val) const deepPointer = getPointer(rootPointer)
lodashSet(obj, getPointerParts(deepPointer).path, val)
} }

View file

@ -1,3 +1,6 @@
/**
* Just an empty function
*/
const noop = () => {} const noop = () => {}
export default noop export default noop

View file

@ -1,4 +1,3 @@
import type {IUtilContext} from '@theatre/shared/logger'
import { import {
getLastMultipleOf, getLastMultipleOf,
numberOfDecimals, numberOfDecimals,
@ -28,44 +27,36 @@ const example = <Args extends $IntentionalAny[], Return>(
) )
} }
const CTX: IUtilContext = {
get logger(): never {
throw new Error('unexpected logger access in test example')
},
}
describe(`numberRoundingUtils()`, () => { describe(`numberRoundingUtils()`, () => {
describe(`roundestNumberBetween()`, () => { describe(`roundestNumberBetween()`, () => {
example(roundestNumberBetween, [CTX, 0.1, 1.1], 1) example(roundestNumberBetween, [0.1, 1.1], 1)
example(roundestNumberBetween, [CTX, 0.1111111123, 0.2943439448], 0.25) example(roundestNumberBetween, [0.1111111123, 0.2943439448], 0.25)
example(roundestNumberBetween, [CTX, 0.19, 0.23], 0.2) example(roundestNumberBetween, [0.19, 0.23], 0.2)
example(roundestNumberBetween, [CTX, -0.19, 0.23], 0) example(roundestNumberBetween, [-0.19, 0.23], 0)
example(roundestNumberBetween, [CTX, -0.19, -0.02], -0.1, {debug: false}) example(roundestNumberBetween, [-0.19, -0.02], -0.1, {debug: false})
example(roundestNumberBetween, [CTX, -0.19, -0.022], -0.1, {debug: false}) example(roundestNumberBetween, [-0.19, -0.022], -0.1, {debug: false})
example(roundestNumberBetween, [CTX, -0.19, -0.022234324], -0.1, { example(roundestNumberBetween, [-0.19, -0.022234324], -0.1, {debug: false})
debug: false, example(roundestNumberBetween, [-0.19, 0.0222222], 0)
}) example(roundestNumberBetween, [-0.19, 0.02], 0)
example(roundestNumberBetween, [CTX, -0.19, 0.0222222], 0)
example(roundestNumberBetween, [CTX, -0.19, 0.02], 0)
example( example(
roundestNumberBetween, roundestNumberBetween,
[CTX, 22304.2398427391, 22304.2398427393], [22304.2398427391, 22304.2398427393],
22304.2398427392, 22304.2398427392,
) )
example(roundestNumberBetween, [CTX, 22304.2398427391, 22304.4], 22304.25) example(roundestNumberBetween, [22304.2398427391, 22304.4], 22304.25)
example(roundestNumberBetween, [CTX, 902, 901], 902) example(roundestNumberBetween, [902, 901], 902)
example(roundestNumberBetween, [CTX, -10, -5], -10) example(roundestNumberBetween, [-10, -5], -10)
example(roundestNumberBetween, [CTX, -5, -10], -10) example(roundestNumberBetween, [-5, -10], -10)
example(roundestNumberBetween, [CTX, -10, -5], -10) example(roundestNumberBetween, [-10, -5], -10)
example( example(
roundestNumberBetween, roundestNumberBetween,
[CTX, -0.00876370109231405, -2.909374013346118e-50], [-0.00876370109231405, -2.909374013346118e-50],
0, 0,
{debug: false}, {debug: false},
) )
example( example(
roundestNumberBetween, roundestNumberBetween,
[CTX, 0.059449443526800295, 0.06682093143783596], [0.059449443526800295, 0.06682093143783596],
0.06, 0.06,
{debug: false}, {debug: false},
) )
@ -82,7 +73,7 @@ describe(`numberRoundingUtils()`, () => {
const from = toPrecision(getRandomNumber()) const from = toPrecision(getRandomNumber())
const to = toPrecision(getRandomNumber()) const to = toPrecision(getRandomNumber())
const result = roundestNumberBetween(CTX, from, to) const result = roundestNumberBetween(from, to)
if (from < to) { if (from < to) {
if (result < from || result > to) { if (result < from || result > to) {
throw new Error(`Invalid: ${from} ${to} ${result}`) throw new Error(`Invalid: ${from} ${to} ${result}`)
@ -96,43 +87,43 @@ describe(`numberRoundingUtils()`, () => {
}) })
}) })
describe(`roundestIntegerBetween`, () => { describe(`roundestIntegerBetween`, () => {
example(roundestIntegerBetween, [CTX, -1, 6], 0, {}) example(roundestIntegerBetween, [-1, 6], 0, {})
example(roundestIntegerBetween, [CTX, 0, 6], 0, {}) example(roundestIntegerBetween, [0, 6], 0, {})
example(roundestIntegerBetween, [CTX, -1, 0], 0, {}) example(roundestIntegerBetween, [-1, 0], 0, {})
example(roundestIntegerBetween, [CTX, -1850, -1740], -1750, {}) example(roundestIntegerBetween, [-1850, -1740], -1750, {})
example(roundestIntegerBetween, [CTX, 1, 6], 5, {}) example(roundestIntegerBetween, [1, 6], 5, {})
example(roundestIntegerBetween, [CTX, 1, 5], 5) example(roundestIntegerBetween, [1, 5], 5)
example(roundestIntegerBetween, [CTX, 1, 2], 2) example(roundestIntegerBetween, [1, 2], 2)
example(roundestIntegerBetween, [CTX, 1, 10], 10) example(roundestIntegerBetween, [1, 10], 10)
example(roundestIntegerBetween, [CTX, 1, 12], 10) example(roundestIntegerBetween, [1, 12], 10)
example(roundestIntegerBetween, [CTX, 11, 15], 15) example(roundestIntegerBetween, [11, 15], 15)
example(roundestIntegerBetween, [CTX, 101, 102], 102, {debug: true}) example(roundestIntegerBetween, [101, 102], 102, {debug: true})
example(roundestIntegerBetween, [CTX, 11, 14, false], 12) example(roundestIntegerBetween, [11, 14, false], 12)
example(roundestIntegerBetween, [CTX, 11, 14, true], 12.5) example(roundestIntegerBetween, [11, 14, true], 12.5)
example(roundestIntegerBetween, [CTX, 11, 12], 12) example(roundestIntegerBetween, [11, 12], 12)
example(roundestIntegerBetween, [CTX, 11, 12], 12, {}) example(roundestIntegerBetween, [11, 12], 12, {})
example(roundestIntegerBetween, [CTX, 10, 90], 50) example(roundestIntegerBetween, [10, 90], 50)
example(roundestIntegerBetween, [CTX, 10, 100], 100) example(roundestIntegerBetween, [10, 100], 100)
example(roundestIntegerBetween, [CTX, 10, 110], 100) example(roundestIntegerBetween, [10, 110], 100)
example(roundestIntegerBetween, [CTX, 9, 100], 10) example(roundestIntegerBetween, [9, 100], 10)
example(roundestIntegerBetween, [CTX, 9, 1100], 10) example(roundestIntegerBetween, [9, 1100], 10)
example(roundestIntegerBetween, [CTX, 9, 699], 10) example(roundestIntegerBetween, [9, 699], 10)
example(roundestIntegerBetween, [CTX, 9, 400], 10) example(roundestIntegerBetween, [9, 400], 10)
example(roundestIntegerBetween, [CTX, 9, 199], 10) example(roundestIntegerBetween, [9, 199], 10)
example(roundestIntegerBetween, [CTX, 9, 1199], 10) example(roundestIntegerBetween, [9, 1199], 10)
example(roundestIntegerBetween, [CTX, 1921, 1998], 1950) example(roundestIntegerBetween, [1921, 1998], 1950)
example(roundestIntegerBetween, [CTX, 1921, 2020], 2000) example(roundestIntegerBetween, [1921, 2020], 2000)
example(roundestIntegerBetween, [CTX, 1601, 1998], 1750) example(roundestIntegerBetween, [1601, 1998], 1750)
example(roundestIntegerBetween, [CTX, 1919, 1921], 1920) example(roundestIntegerBetween, [1919, 1921], 1920)
example(roundestIntegerBetween, [CTX, 1919, 1919], 1919) example(roundestIntegerBetween, [1919, 1919], 1919)
example(roundestIntegerBetween, [CTX, 3901, 3902], 3902) example(roundestIntegerBetween, [3901, 3902], 3902)
example(roundestIntegerBetween, [CTX, 901, 902], 902) example(roundestIntegerBetween, [901, 902], 902)
}) })
describe(`roundestFloat()`, () => { describe(`roundestFloat()`, () => {
example(roundestFloat, [CTX, 0.19, 0.2122], 0.2) example(roundestFloat, [0.19, 0.2122], 0.2)
example(roundestFloat, [CTX, 0.19, 0.31], 0.25) example(roundestFloat, [0.19, 0.31], 0.25)
example(roundestFloat, [CTX, 0.19, 0.41], 0.25) example(roundestFloat, [0.19, 0.41], 0.25)
example(roundestFloat, [CTX, 0.19, 1.9], 0.5) example(roundestFloat, [0.19, 1.9], 0.5)
}) })
describe(`numberOfDecimals()`, () => { describe(`numberOfDecimals()`, () => {
example(numberOfDecimals, [1.1], 1) example(numberOfDecimals, [1.1], 1)

View file

@ -1,29 +1,39 @@
import padEnd from 'lodash-es/padEnd' import padEnd from 'lodash-es/padEnd'
import type {IUtilContext} from '@theatre/shared/logger' import logger from '@theatre/shared/logger'
export function roundestNumberBetween( /**
ctx: IUtilContext, * Returns the _roundest_ number `c`, such that `a <= c <= b`. This is useful
_a: number, * when a number value is beinged "nudged" by a user, and we want to avoid setting
_b: number, * it to weird value like `101.1239293814314`, when we know that the user probably just meant `100`.
): number { *
* Examples
* ```ts
* roundestNumberBetween(0.1111111123, 0.2943439448) // 0.25
* roundestNumberBetween(0.19, 0.23) // 0.2
* roundestNumberBetween(1921, 1998) // 1950
* roundestNumberBetween(10, 110) // 100
* // There are many more examples at `./numberRoundingUtils.test.ts`
* ```
*/
export function roundestNumberBetween(_a: number, _b: number): number {
if (_b < _a) { if (_b < _a) {
return roundestNumberBetween(ctx, _b, _a) return roundestNumberBetween(_b, _a)
} }
if (_a < 0 && _b < 0) { if (_a < 0 && _b < 0) {
return noMinusZero(roundestNumberBetween(ctx, -_b, -_a) * -1) return noMinusZero(roundestNumberBetween(-_b, -_a) * -1)
} }
if (_a <= 0 && _b >= 0) return 0 if (_a <= 0 && _b >= 0) return 0
const aCeiling = Math.ceil(_a) const aCeiling = Math.ceil(_a)
if (aCeiling <= _b) { if (aCeiling <= _b) {
return roundestIntegerBetween(ctx, aCeiling, Math.floor(_b)) return roundestIntegerBetween(aCeiling, Math.floor(_b))
} else { } else {
const [a, b] = [_a, _b] const [a, b] = [_a, _b]
const integer = Math.floor(a) const integer = Math.floor(a)
return integer + roundestFloat(ctx, a - integer, b - integer) return integer + roundestFloat(a - integer, b - integer)
} }
} }
@ -32,7 +42,6 @@ const halvesAndQuartiles = [5, 2.5, 7.5]
const multipliersWithoutQuartiles = [5, 2, 4, 6, 8, 1, 3, 7, 9] const multipliersWithoutQuartiles = [5, 2, 4, 6, 8, 1, 3, 7, 9]
export function roundestIntegerBetween( export function roundestIntegerBetween(
ctx: IUtilContext,
_a: number, _a: number,
_b: number, _b: number,
decimalsAllowed: boolean = true, decimalsAllowed: boolean = true,
@ -82,9 +91,7 @@ export function roundestIntegerBetween(
base = highestTotalFound base = highestTotalFound
if (currentExponentiationOfTen === 1) { if (currentExponentiationOfTen === 1) {
ctx.logger.error( logger.error(`Coudn't find a human-readable number between ${a} and ${b}`)
`Coudn't find a human-readable number between ${a} and ${b}`,
)
return _a return _a
} else { } else {
currentExponentiationOfTen /= 10 currentExponentiationOfTen /= 10
@ -133,11 +140,7 @@ export const stringifyNumber = (n: number): string => {
/** /**
* it is expected that both args are 0 \< arg \< 1 * it is expected that both args are 0 \< arg \< 1
*/ */
export const roundestFloat = ( export const roundestFloat = (a: number, b: number): number => {
ctx: IUtilContext,
a: number,
b: number,
): number => {
const inString = { const inString = {
a: stringifyNumber(a), a: stringifyNumber(a),
b: stringifyNumber(b), b: stringifyNumber(b),
@ -171,7 +174,6 @@ export const roundestFloat = (
} }
const roundestInt = roundestIntegerBetween( const roundestInt = roundestIntegerBetween(
ctx,
parseInt(withPaddedDecimals.a, 10) * Math.pow(10, maxNumberOfLeadingZeros), parseInt(withPaddedDecimals.a, 10) * Math.pow(10, maxNumberOfLeadingZeros),
parseInt(withPaddedDecimals.b, 10) * Math.pow(10, maxNumberOfLeadingZeros), parseInt(withPaddedDecimals.b, 10) * Math.pow(10, maxNumberOfLeadingZeros),
true, true,