@gkucmierz/utils 1.24.0 → 1.26.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/package-lock.json +2 -2
- package/package.json +1 -1
- package/spec/Trie.spec.mjs +36 -0
- package/spec/binary-search.spec.mjs +76 -0
- package/src/Trie.mjs +45 -0
- package/src/binary-search.mjs +46 -0
- package/src/copy-case.mjs +6 -1
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gkucmierz/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@gkucmierz/utils",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.26.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"husky": "^8.0.1",
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
Trie,
|
|
4
|
+
} from '../src/Trie.mjs';
|
|
5
|
+
|
|
6
|
+
describe('Trie', () => {
|
|
7
|
+
it('check empty', () => {
|
|
8
|
+
const trie = Trie();
|
|
9
|
+
expect(trie.has('')).toBe(false);
|
|
10
|
+
expect(trie.has('abc')).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('init data', () => {
|
|
14
|
+
const trie = Trie(['abc', '']);
|
|
15
|
+
expect(trie.has('')).toBe(true);
|
|
16
|
+
expect(trie.has('abc')).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('add/readd data', () => {
|
|
20
|
+
const trie = Trie();
|
|
21
|
+
expect(trie.has('')).toBe(false);
|
|
22
|
+
expect(trie.add('')).toBe(true);
|
|
23
|
+
expect(trie.has('')).toBe(true);
|
|
24
|
+
expect(trie.add('')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('list data', () => {
|
|
28
|
+
const trie = Trie(['abc', '', 'abcdef', 'xyz']);
|
|
29
|
+
const abc = ['abc', 'abcdef'];
|
|
30
|
+
expect(trie.get('a')).toEqual(abc);
|
|
31
|
+
expect(trie.get('ab')).toEqual(abc);
|
|
32
|
+
expect(trie.get('abc')).toEqual(abc);
|
|
33
|
+
expect(trie.get('aa')).toEqual([]);
|
|
34
|
+
expect(trie.get('')).toEqual(['', 'abc', 'abcdef', 'xyz']);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
import {
|
|
3
3
|
binarySearchArr,
|
|
4
|
+
binarySearchLE,
|
|
5
|
+
binarySearchGE,
|
|
6
|
+
binarySearchRangeIncl,
|
|
4
7
|
} from '../src/binary-search.mjs';
|
|
5
8
|
|
|
6
9
|
describe('binarySearchArr', () => {
|
|
@@ -13,3 +16,76 @@ describe('binarySearchArr', () => {
|
|
|
13
16
|
expect(binarySearchArr([1, 2, 3, 4], 5)).toBe(-1);
|
|
14
17
|
});
|
|
15
18
|
});
|
|
19
|
+
|
|
20
|
+
describe('binarySearchLE', () => {
|
|
21
|
+
it('mid group', () => {
|
|
22
|
+
const arr = [0,1,2,2,3];
|
|
23
|
+
expect(binarySearchLE(arr, -100)).toBe(-1);
|
|
24
|
+
expect(binarySearchLE(arr, 0)).toBe(0);
|
|
25
|
+
expect(binarySearchLE(arr, 1)).toBe(1);
|
|
26
|
+
expect(binarySearchLE(arr, 2)).toBe(3);
|
|
27
|
+
expect(binarySearchLE(arr, 3)).toBe(4);
|
|
28
|
+
expect(binarySearchLE(arr, 100)).toBe(4);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('begin group', () => {
|
|
32
|
+
const arr = [2,2,3];
|
|
33
|
+
expect(binarySearchLE(arr, -100)).toBe(-1);
|
|
34
|
+
expect(binarySearchLE(arr, 2)).toBe(1);
|
|
35
|
+
expect(binarySearchLE(arr, 3)).toBe(2);
|
|
36
|
+
expect(binarySearchLE(arr, 100)).toBe(2);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('end group', () => {
|
|
40
|
+
const arr = [1,2,2];
|
|
41
|
+
expect(binarySearchLE(arr, -100)).toBe(-1);
|
|
42
|
+
expect(binarySearchLE(arr, 1)).toBe(0);
|
|
43
|
+
expect(binarySearchLE(arr, 2)).toBe(2);
|
|
44
|
+
expect(binarySearchLE(arr, 100)).toBe(2);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('binarySearchGE', () => {
|
|
49
|
+
it('mid group', () => {
|
|
50
|
+
const arr = [0,1,2,2,3];
|
|
51
|
+
expect(binarySearchGE(arr, -100)).toBe(0);
|
|
52
|
+
expect(binarySearchGE(arr, 0)).toBe(0);
|
|
53
|
+
expect(binarySearchGE(arr, 1)).toBe(1);
|
|
54
|
+
expect(binarySearchGE(arr, 2)).toBe(2);
|
|
55
|
+
expect(binarySearchGE(arr, 3)).toBe(4);
|
|
56
|
+
expect(binarySearchGE(arr, 100)).toBe(5);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('begin group', () => {
|
|
60
|
+
const arr = [2,2,3];
|
|
61
|
+
expect(binarySearchGE(arr, -100)).toBe(0);
|
|
62
|
+
expect(binarySearchGE(arr, 2)).toBe(0);
|
|
63
|
+
expect(binarySearchGE(arr, 3)).toBe(2);
|
|
64
|
+
expect(binarySearchGE(arr, 100)).toBe(3);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('end group', () => {
|
|
68
|
+
const arr = [1,2,2];
|
|
69
|
+
expect(binarySearchGE(arr, -100)).toBe(0);
|
|
70
|
+
expect(binarySearchGE(arr, 1)).toBe(0);
|
|
71
|
+
expect(binarySearchGE(arr, 2)).toBe(1);
|
|
72
|
+
expect(binarySearchGE(arr, 100)).toBe(3);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('binarySearchRangeIncl', () => {
|
|
77
|
+
it('basic', () => {
|
|
78
|
+
const arr = [0,1,1,3];
|
|
79
|
+
const check = [-10, ...arr, 10];
|
|
80
|
+
const expected = [
|
|
81
|
+
[0, -1],
|
|
82
|
+
[0, 0], [1, 2], [1, 2], [3, 3],
|
|
83
|
+
[4, 3],
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < check.length; ++i) {
|
|
87
|
+
expect(binarySearchRangeIncl(arr, check[i])).toEqual(expected[i]);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
package/src/Trie.mjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
export const Trie = (words = []) => {
|
|
3
|
+
const HAS = 0;
|
|
4
|
+
const MAP = 1;
|
|
5
|
+
const data = [false, new Map()];
|
|
6
|
+
const add = word => {
|
|
7
|
+
let node = data;
|
|
8
|
+
for (let i = 0; i < word.length; ++i) {
|
|
9
|
+
const char = word[i];
|
|
10
|
+
if (!node[MAP]) node[MAP] = new Map();
|
|
11
|
+
const child = node[MAP].get(char) ?? [false];
|
|
12
|
+
node[MAP].set(char, child);
|
|
13
|
+
node = child;
|
|
14
|
+
}
|
|
15
|
+
if (node[HAS]) return false;
|
|
16
|
+
node[HAS] = true;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
const findNode = word => {
|
|
20
|
+
let node = data;
|
|
21
|
+
for (let i = 0; i < word.length; ++i) {
|
|
22
|
+
const char = word[i];
|
|
23
|
+
if (!node[MAP]?.has(char)) return [false];
|
|
24
|
+
node = node[MAP].get(char);
|
|
25
|
+
}
|
|
26
|
+
return node;
|
|
27
|
+
};
|
|
28
|
+
const has = word => findNode(word)[HAS];
|
|
29
|
+
const get = begin => {
|
|
30
|
+
const res = [];
|
|
31
|
+
const loop = (node, str = '') => {
|
|
32
|
+
if (!node) return;
|
|
33
|
+
if (node[HAS]) res.push(begin + str);
|
|
34
|
+
const map = node[MAP] || new Map();
|
|
35
|
+
[...map].map(([char, node]) => loop(node, str + char));
|
|
36
|
+
};
|
|
37
|
+
loop(findNode(begin));
|
|
38
|
+
return res;
|
|
39
|
+
};
|
|
40
|
+
words.map(word => add(word));
|
|
41
|
+
return {
|
|
42
|
+
add, has, get,
|
|
43
|
+
getData: () => data,
|
|
44
|
+
};
|
|
45
|
+
};
|
package/src/binary-search.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
|
|
2
|
+
// binary search array exact element
|
|
3
|
+
// in sorted array
|
|
2
4
|
export const binarySearchArr = (arr, target) => {
|
|
3
5
|
let [a, b] = [0, arr.length];
|
|
4
6
|
let lm;
|
|
@@ -17,3 +19,47 @@ export const binarySearchArr = (arr, target) => {
|
|
|
17
19
|
}
|
|
18
20
|
return -1;
|
|
19
21
|
};
|
|
22
|
+
|
|
23
|
+
// binary search array less or equal element
|
|
24
|
+
export const binarySearchLE = (arr, target) => {
|
|
25
|
+
let [a, b] = [0, arr.length];
|
|
26
|
+
let lm;
|
|
27
|
+
while (b - a > 0) {
|
|
28
|
+
const mid = (a + b) / 2 | 0;
|
|
29
|
+
const val = arr[mid];
|
|
30
|
+
if (target < val) {
|
|
31
|
+
b = mid;
|
|
32
|
+
} else if (val <= target) {
|
|
33
|
+
a = mid;
|
|
34
|
+
}
|
|
35
|
+
if (lm === mid) return mid;
|
|
36
|
+
lm = mid;
|
|
37
|
+
}
|
|
38
|
+
return -1;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// binary search array greater or equal element
|
|
42
|
+
export const binarySearchGE = (arr, target) => {
|
|
43
|
+
let [a, b] = [0, arr.length];
|
|
44
|
+
let lm;
|
|
45
|
+
while (b - a > 0) {
|
|
46
|
+
const mid = (a + b) / 2 | 0;
|
|
47
|
+
const val = arr[mid];
|
|
48
|
+
if (target > val) {
|
|
49
|
+
a = mid;
|
|
50
|
+
} else if (val >= target) {
|
|
51
|
+
b = mid;
|
|
52
|
+
}
|
|
53
|
+
if (lm === mid) return mid + 1;
|
|
54
|
+
lm = mid;
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// binary search for range of elements in array inclusive
|
|
60
|
+
export const binarySearchRangeIncl = (arr, target) => {
|
|
61
|
+
return [
|
|
62
|
+
binarySearchGE(arr, target),
|
|
63
|
+
binarySearchLE(arr, target),
|
|
64
|
+
];
|
|
65
|
+
};
|
package/src/copy-case.mjs
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Copy the case of a string into another.
|
|
4
|
+
* @param {string} target String to change the case.
|
|
5
|
+
* @param {string} source Source of case pattern.
|
|
6
|
+
* @return {string} Converted string.
|
|
7
|
+
*/
|
|
3
8
|
export const copyCase = (word, from) => {
|
|
4
9
|
const isLower = w => w.toLowerCase() === w;
|
|
5
10
|
const isUpper = w => w.toUpperCase() === w;
|