extra-utils 5.14.0 → 5.16.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.
package/README.md CHANGED
@@ -87,6 +87,40 @@ function remap(
87
87
  , [newMin, newMax]: readonly [newMin: number, newMax: number]
88
88
  ): number
89
89
 
90
+ function remapToItem<T>(
91
+ value: number
92
+ , range: readonly [min: number, max: number]
93
+ , items: NonEmptyArray<T>
94
+ ): T
95
+
96
+ interface IWeightedItem {
97
+ weight: number
98
+ }
99
+
100
+ function remapToWeightedItem<T>(
101
+ value: number
102
+ , range: readonly [min: number, max: number]
103
+ , items: NonEmptyArray<T>
104
+ , weights: NonEmptyArray<number>
105
+ ): T
106
+ function remapToWeightedItem<T extends IWeightedItem>(
107
+ value: number
108
+ , range: readonly [min: number, max: number]
109
+ , items: NonEmptyArray<T>
110
+ ): T
111
+
112
+ function remapToIndex(
113
+ value: number
114
+ , range: readonly [min: number, max: number]
115
+ , items: NonEmptyArray<unknown>
116
+ ): number
117
+
118
+ function remapToWeightedIndex(
119
+ value: number
120
+ , range: [min: number, max: number]
121
+ , weights: NonEmptyArray<number>
122
+ ): number
123
+
90
124
  function lerp(
91
125
  alpha: number
92
126
  , [from, to]: readonly [from: number, to: number]
@@ -5,4 +5,8 @@ export * from './is-nan.js';
5
5
  export * from './is-number.js';
6
6
  export * from './clamp.js';
7
7
  export * from './remap.js';
8
+ export * from './remap-to-item.js';
9
+ export * from './remap-to-weighted-item.js';
10
+ export * from './remap-to-index.js';
11
+ export * from './remap-to-weighted-index.js';
8
12
  export * from './lerp.js';
@@ -5,5 +5,9 @@ export * from './is-nan.js';
5
5
  export * from './is-number.js';
6
6
  export * from './clamp.js';
7
7
  export * from './remap.js';
8
+ export * from './remap-to-item.js';
9
+ export * from './remap-to-weighted-item.js';
10
+ export * from './remap-to-index.js';
11
+ export * from './remap-to-weighted-index.js';
8
12
  export * from './lerp.js';
9
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,qBAAqB,CAAA;AACnC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,WAAW,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { NonEmptyArray } from 'justypes';
2
+ export declare function remapToIndex(value: number, range: readonly [min: number, max: number], items: NonEmptyArray<unknown>): number;
@@ -0,0 +1,8 @@
1
+ import { remap } from './remap.js';
2
+ export function remapToIndex(value, range, items) {
3
+ const index = Math.floor(remap(value, range, [0, items.length]));
4
+ return index === items.length
5
+ ? items.length - 1
6
+ : index;
7
+ }
8
+ //# sourceMappingURL=remap-to-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remap-to-index.js","sourceRoot":"","sources":["../../src/number/remap-to-index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,KAA0C,EAC1C,KAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,KAAK,CACH,KAAK,EACL,KAAK,EACL,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAClB,CACF,CAAA;IAED,OAAO,KAAK,KAAK,KAAK,CAAC,MAAM;QACxB,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAClB,CAAC,CAAC,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { NonEmptyArray } from 'justypes';
2
+ export declare function remapToItem<T>(value: number, range: readonly [min: number, max: number], items: NonEmptyArray<T>): T;
@@ -0,0 +1,6 @@
1
+ import { remapToIndex } from './remap-to-index.js';
2
+ export function remapToItem(value, range, items) {
3
+ const index = remapToIndex(value, range, items);
4
+ return items[index];
5
+ }
6
+ //# sourceMappingURL=remap-to-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remap-to-item.js","sourceRoot":"","sources":["../../src/number/remap-to-item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,KAA0C,EAC1C,KAAuB;IAEvB,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;IAC/C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;AACrB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { NonEmptyArray } from 'justypes';
2
+ export declare function remapToWeightedIndex(value: number, range: readonly [min: number, max: number], weights: NonEmptyArray<number>): number;
@@ -0,0 +1,25 @@
1
+ import { remap } from './remap.js';
2
+ import { remapToIndex } from './remap-to-index.js';
3
+ export function remapToWeightedIndex(value, range, weights) {
4
+ const newRangeMax = weights.reduce((acc, cur) => acc + Math.max(cur, 0));
5
+ if (newRangeMax === 0) {
6
+ return remapToIndex(value, range, weights);
7
+ }
8
+ else {
9
+ const newValue = remap(value, range, [0, newRangeMax]);
10
+ let remains = newRangeMax;
11
+ for (let i = weights.length; i--;) {
12
+ const weight = weights[i];
13
+ if (weight <= 0) {
14
+ continue;
15
+ }
16
+ else {
17
+ remains -= weight;
18
+ if (newValue >= remains)
19
+ return i;
20
+ }
21
+ }
22
+ throw new Error('Impossible route');
23
+ }
24
+ }
25
+ //# sourceMappingURL=remap-to-weighted-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remap-to-weighted-index.js","sourceRoot":"","sources":["../../src/number/remap-to-weighted-index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,UAAU,oBAAoB,CAClC,KAAa,EACb,KAA0C,EAC1C,OAA8B;IAE9B,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;IAExE,IAAI,WAAW,KAAK,CAAC,EAAE;QAErB,OAAO,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;KAC3C;SAAM;QACL,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAA;QAEtD,IAAI,OAAO,GAAG,WAAW,CAAA;QACzB,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG;YACjC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YACzB,IAAI,MAAM,IAAI,CAAC,EAAE;gBACf,SAAQ;aACT;iBAAM;gBACL,OAAO,IAAI,MAAM,CAAA;gBACjB,IAAI,QAAQ,IAAI,OAAO;oBAAE,OAAO,CAAC,CAAA;aAClC;SACF;QAED,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;KACpC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { NonEmptyArray } from 'justypes';
2
+ export interface IWeightedItem {
3
+ weight: number;
4
+ }
5
+ export declare function remapToWeightedItem<T>(value: number, range: readonly [min: number, max: number], items: NonEmptyArray<T>, weights: NonEmptyArray<number>): T;
6
+ export declare function remapToWeightedItem<T extends IWeightedItem>(value: number, range: readonly [min: number, max: number], items: NonEmptyArray<T>): T;
@@ -0,0 +1,17 @@
1
+ import { remapToWeightedIndex } from './remap-to-weighted-index.js';
2
+ export function remapToWeightedItem(...args) {
3
+ let value;
4
+ let range;
5
+ let items;
6
+ let weights;
7
+ if (args.length === 3) {
8
+ [value, range, items] = args;
9
+ weights = items.map(item => item.weight);
10
+ }
11
+ else {
12
+ [value, range, items, weights] = args;
13
+ }
14
+ const index = remapToWeightedIndex(value, range, weights);
15
+ return items[index];
16
+ }
17
+ //# sourceMappingURL=remap-to-weighted-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remap-to-weighted-item.js","sourceRoot":"","sources":["../../src/number/remap-to-weighted-item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAiBnE,MAAM,UAAU,mBAAmB,CAA0B,GAAG,IAW7D;IAED,IAAI,KAAa,CAAA;IACjB,IAAI,KAA0C,CAAA;IAC9C,IAAI,KAAuB,CAAA;IAC3B,IAAI,OAA8B,CAAA;IAElC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACrB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAA;QAC5B,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAA0B,CAAA;KAClE;SAAM;QACL,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KACtC;IAED,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACzD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extra-utils",
3
- "version": "5.14.0",
3
+ "version": "5.16.0",
4
4
  "description": "Utilities for JavaScript and Typescript",
5
5
  "files": [
6
6
  "src",
@@ -5,4 +5,8 @@ export * from './is-nan.js'
5
5
  export * from './is-number.js'
6
6
  export * from './clamp.js'
7
7
  export * from './remap.js'
8
+ export * from './remap-to-item.js'
9
+ export * from './remap-to-weighted-item.js'
10
+ export * from './remap-to-index.js'
11
+ export * from './remap-to-weighted-index.js'
8
12
  export * from './lerp.js'
@@ -0,0 +1,20 @@
1
+ import { NonEmptyArray } from 'justypes'
2
+ import { remap } from './remap.js'
3
+
4
+ export function remapToIndex(
5
+ value: number
6
+ , range: readonly [min: number, max: number]
7
+ , items: NonEmptyArray<unknown>
8
+ ): number {
9
+ const index = Math.floor(
10
+ remap(
11
+ value
12
+ , range
13
+ , [0, items.length]
14
+ )
15
+ )
16
+
17
+ return index === items.length
18
+ ? items.length - 1
19
+ : index
20
+ }
@@ -0,0 +1,11 @@
1
+ import { NonEmptyArray } from 'justypes'
2
+ import { remapToIndex } from './remap-to-index.js'
3
+
4
+ export function remapToItem<T>(
5
+ value: number
6
+ , range: readonly [min: number, max: number]
7
+ , items: NonEmptyArray<T>
8
+ ): T {
9
+ const index = remapToIndex(value, range, items)
10
+ return items[index]
11
+ }
@@ -0,0 +1,31 @@
1
+ import { NonEmptyArray } from 'justypes'
2
+ import { remap } from './remap.js'
3
+ import { remapToIndex } from './remap-to-index.js'
4
+
5
+ export function remapToWeightedIndex(
6
+ value: number
7
+ , range: readonly [min: number, max: number]
8
+ , weights: NonEmptyArray<number>
9
+ ): number {
10
+ const newRangeMax = weights.reduce((acc, cur) => acc + Math.max(cur, 0))
11
+
12
+ if (newRangeMax === 0) {
13
+ // 只有在所有权重都小于等于0的情况下会进入此路径, 所有权重此时都被视为1.
14
+ return remapToIndex(value, range, weights)
15
+ } else {
16
+ const newValue = remap(value, range, [0, newRangeMax])
17
+
18
+ let remains = newRangeMax
19
+ for (let i = weights.length; i--;) {
20
+ const weight = weights[i]
21
+ if (weight <= 0) {
22
+ continue
23
+ } else {
24
+ remains -= weight
25
+ if (newValue >= remains) return i
26
+ }
27
+ }
28
+
29
+ throw new Error('Impossible route')
30
+ }
31
+ }
@@ -0,0 +1,46 @@
1
+ import { NonEmptyArray } from 'justypes'
2
+ import { remapToWeightedIndex } from './remap-to-weighted-index.js'
3
+
4
+ export interface IWeightedItem {
5
+ weight: number
6
+ }
7
+
8
+ export function remapToWeightedItem<T>(
9
+ value: number
10
+ , range: readonly [min: number, max: number]
11
+ , items: NonEmptyArray<T>
12
+ , weights: NonEmptyArray<number>
13
+ ): T
14
+ export function remapToWeightedItem<T extends IWeightedItem>(
15
+ value: number
16
+ , range: readonly [min: number, max: number]
17
+ , items: NonEmptyArray<T>
18
+ ): T
19
+ export function remapToWeightedItem<T extends IWeightedItem>(...args:
20
+ | [
21
+ value: number
22
+ , range: readonly [min: number, max: number]
23
+ , items: NonEmptyArray<T>
24
+ , weights: NonEmptyArray<number>
25
+ ]
26
+ | [
27
+ value: number
28
+ , range: readonly [min: number, max: number]
29
+ , items: NonEmptyArray<T>
30
+ ]
31
+ ): T {
32
+ let value: number
33
+ let range: readonly [min: number, max: number]
34
+ let items: NonEmptyArray<T>
35
+ let weights: NonEmptyArray<number>
36
+
37
+ if (args.length === 3) {
38
+ [value, range, items] = args
39
+ weights = items.map(item => item.weight) as NonEmptyArray<number>
40
+ } else {
41
+ [value, range, items, weights] = args
42
+ }
43
+
44
+ const index = remapToWeightedIndex(value, range, weights)
45
+ return items[index]
46
+ }