Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new-log-viewer: Add bounds checking and search methods to support log filtering around a specific log event. #81

Merged
merged 16 commits into from
Oct 3, 2024
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.
Comment on lines +68 to +69
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Finds the largest index `i` in a sorted array `data` such that `data[i] <= target`. Uses binary
* search for efficiency.
* Finds the index `i` in a sorted array `data` such that `data[i]` is the "first nearest" element
* to `target`, where "first nearest" is defined as follows:
* - If `target` is in or greater than the range of `data`'s elements, then `i` is the largest index
* such that `data[i] <= target`.
* - Otherwise, `target` is less than the range of `data`'s elements, so `i` is 0 since `data[0]` is
* the array element closest to `target`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see review comment

*
* @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,
};
Loading