Skip to content

Commit 55d250a

Browse files
authored
new-log-viewer: Add bounds checking and search methods to support log filtering around a specific log event. (#81)
1 parent 5bc746a commit 55d250a

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

new-log-viewer/src/utils/data.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,100 @@
11
import {Nullable} from "../typings/common";
2+
import {clamp} from "./math";
23

34

5+
/**
6+
* Checks if `target` is bounded by the first and last value in a sorted array of numbers.
7+
*
8+
* @param data An array sorted in ascending order.
9+
* @param target
10+
* @return Whether `target` is within the bounds of the array's values.
11+
*/
12+
const isWithinBounds = (data: number[], target: number): boolean => {
13+
const {length} = data;
14+
if (0 === length) {
15+
return false;
16+
}
17+
18+
return (target >= (data[0] as number)) && (target <= (data[length - 1] as number));
19+
};
20+
21+
/**
22+
* Clamps a number using the first and last value in a sorted array of numbers.
23+
*
24+
* @param data An array sorted in ascending order.
25+
* @param num The number to be clamped.
26+
* @return The clamped number.
27+
*/
28+
const clampWithinBounds = (data: number[], num: number): number => {
29+
const {length} = data;
30+
if (0 === length) {
31+
return num;
32+
}
33+
34+
return clamp(num, data[0] as number, data[length - 1] as number);
35+
};
36+
37+
/**
38+
* Performs binary search to find the smallest index `i` in the range `[0, length)` where
39+
* `predicate` is true. `predicate` should be false for some prefix of the input range and true
40+
* for the remainder.
41+
*
42+
* @param length The length of the range to search.
43+
* @param predicate A function that takes an index and returns `true` or `false`.
44+
* @return The smallest index where `predicate(i)` is true, or `length` if no such index exists.
45+
* @example
46+
* const arr = [1, 3, 5, 7, 10, 15, 20];
47+
* const result = binarySearch(arr.length, (i) => arr[i] >= 10);
48+
* console.log(result); // Output: 4 (since arr[4] is 10).
49+
*/
50+
const binarySearch = (length: number, predicate: (index: number) => boolean): number => {
51+
// Generic implementation based on Go standard library implementation.
52+
// Reference: https://pkg.go.dev/sort#Search
53+
let i = 0;
54+
let j = length;
55+
while (i < j) {
56+
const mid = Math.floor((i + j) / 2);
57+
if (false === predicate(mid)) {
58+
i = mid + 1;
59+
} else {
60+
j = mid;
61+
}
62+
}
63+
64+
return i;
65+
};
66+
67+
/**
68+
* Finds the largest index `i` in a sorted array `data` such that `data[i] <= target`. Uses binary
69+
* search for efficiency.
70+
*
71+
* @param data An array sorted in ascending order.
72+
* @param target
73+
* @return The largest index where `data[i] <= target` or:
74+
* - `length` if no such index exists.
75+
* - `null` if array is empty.
76+
* @example
77+
* const arr = [2, 3, 5, 7, 10, 15, 20];
78+
* const result = findNearestLessThanOrEqualElement(arr, 8);
79+
* console.log(result); // Output: 3 (since arr[3] is 7).
80+
*/
81+
const findNearestLessThanOrEqualElement = (data: number[], target: number): Nullable<number> => {
82+
const {length} = data;
83+
84+
if (0 === length) {
85+
return null;
86+
}
87+
88+
// Binary search to find the first index where data[i] > target.
89+
const firstGreaterIdx: number = binarySearch(length, (i) => data[i] as number > target);
90+
91+
if (0 === firstGreaterIdx) {
92+
return length;
93+
}
94+
95+
return firstGreaterIdx - 1;
96+
};
97+
498
/**
599
* Finds the key in a map based on the provided value.
6100
*
@@ -38,7 +132,11 @@ const getMapValueWithNearestLessThanOrEqualKey = <T>(
38132
map.get(lowerBoundKey) as T;
39133
};
40134

135+
41136
export {
137+
clampWithinBounds,
138+
findNearestLessThanOrEqualElement,
42139
getMapKeyByValue,
43140
getMapValueWithNearestLessThanOrEqualKey,
141+
isWithinBounds,
44142
};

0 commit comments

Comments
 (0)