Skip to content

Commit

Permalink
new-log-viewer: Add bounds checking and search methods to support log…
Browse files Browse the repository at this point in the history
… filtering around a specific log event. (y-scope#81)
  • Loading branch information
davemarco authored Oct 3, 2024
1 parent 5bc746a commit 55d250a
Showing 1 changed file with 98 additions and 0 deletions.
98 changes: 98 additions & 0 deletions new-log-viewer/src/utils/data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,100 @@
import {Nullable} from "../typings/common";
import {clamp} from "./math";


/**
* Checks if `target` is bounded by the first and last value in a sorted array of numbers.
*
* @param data An array sorted in ascending order.
* @param target
* @return Whether `target` is within the bounds of the array's values.
*/
const isWithinBounds = (data: number[], target: number): boolean => {
const {length} = data;
if (0 === length) {
return false;
}

return (target >= (data[0] as number)) && (target <= (data[length - 1] as number));
};

/**
* Clamps a number using the first and last value in a sorted array of numbers.
*
* @param data An array sorted in ascending order.
* @param num The number to be clamped.
* @return The clamped number.
*/
const clampWithinBounds = (data: number[], num: number): number => {
const {length} = data;
if (0 === length) {
return num;
}

return clamp(num, data[0] as number, data[length - 1] as number);
};

/**
* Performs binary search to find the smallest index `i` in the range `[0, length)` where
* `predicate` is true. `predicate` should be false for some prefix of the input range and true
* for the remainder.
*
* @param length The length of the range to search.
* @param predicate A function that takes an index and returns `true` or `false`.
* @return The smallest index where `predicate(i)` is true, or `length` if no such index exists.
* @example
* const arr = [1, 3, 5, 7, 10, 15, 20];
* const result = binarySearch(arr.length, (i) => arr[i] >= 10);
* console.log(result); // Output: 4 (since arr[4] is 10).
*/
const binarySearch = (length: number, predicate: (index: number) => boolean): number => {
// Generic implementation based on Go standard library implementation.
// Reference: https://pkg.go.dev/sort#Search
let i = 0;
let j = length;
while (i < j) {
const mid = Math.floor((i + j) / 2);
if (false === predicate(mid)) {
i = mid + 1;
} else {
j = mid;
}
}

return i;
};

/**
* Finds the largest index `i` in a sorted array `data` such that `data[i] <= target`. Uses binary
* search for efficiency.
*
* @param data An array sorted in ascending order.
* @param target
* @return The largest index where `data[i] <= target` or:
* - `length` if no such index exists.
* - `null` if array is empty.
* @example
* const arr = [2, 3, 5, 7, 10, 15, 20];
* const result = findNearestLessThanOrEqualElement(arr, 8);
* console.log(result); // Output: 3 (since arr[3] is 7).
*/
const findNearestLessThanOrEqualElement = (data: number[], target: number): Nullable<number> => {
const {length} = data;

if (0 === length) {
return null;
}

// Binary search to find the first index where data[i] > target.
const firstGreaterIdx: number = binarySearch(length, (i) => data[i] as number > target);

if (0 === firstGreaterIdx) {
return length;
}

return firstGreaterIdx - 1;
};

/**
* Finds the key in a map based on the provided value.
*
Expand Down Expand Up @@ -38,7 +132,11 @@ const getMapValueWithNearestLessThanOrEqualKey = <T>(
map.get(lowerBoundKey) as T;
};


export {
clampWithinBounds,
findNearestLessThanOrEqualElement,
getMapKeyByValue,
getMapValueWithNearestLessThanOrEqualKey,
isWithinBounds,
};

0 comments on commit 55d250a

Please sign in to comment.