All files / core/src/utilities isEqual.ts

71.42% Statements 20/28
64.1% Branches 25/39
70% Functions 7/10
70.37% Lines 19/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111          79949631x               71034x       71034x 169080x 34661x       36373x       159632136x       142068x                                                     79851585x       79851585x 79780551x     71034x 71034x           428x 680x   428x           428x       170x           428x                                    
function areNumbersEqualWithTolerance(
  num1: number,
  num2: number,
  tolerance: number
): boolean {
  return Math.abs(num1 - num2) <= tolerance;
}
 
function areArraysEqual(
  arr1: ArrayLike<number>,
  arr2: ArrayLike<number>,
  tolerance = 1e-5
): boolean {
  Iif (arr1.length !== arr2.length) {
    return false;
  }
 
  for (let i = 0; i < arr1.length; i++) {
    if (!areNumbersEqualWithTolerance(arr1[i], arr2[i], tolerance)) {
      return false;
    }
  }
 
  return true;
}
 
function isNumberType(value: unknown): value is number {
  return typeof value === 'number';
}
 
function isNumberArrayLike(value: unknown): value is ArrayLike<number> {
  return (
    value &&
    typeof value === 'object' &&
    'length' in value &&
    typeof (value as ArrayLike<number>).length === 'number' &&
    (value as ArrayLike<number>).length > 0 &&
    typeof (value as ArrayLike<number>)[0] === 'number'
  );
}
 
/**
 * Returns whether two values are equal or not, based on epsilon comparison.
 * For array comparison, it does NOT strictly compare them but only compare its values.
 * It can compare array of numbers and also typed array. Otherwise it will just return false.
 *
 * @param v1 - The first value to compare
 * @param v2 - The second value to compare
 * @param tolerance - The acceptable tolerance, the default is 0.00001
 *
 * @returns True if the two values are within the tolerance levels.
 */
export function isEqual<ValueType>(
  v1: ValueType,
  v2: ValueType,
  tolerance = 1e-5
): boolean {
  // values must be the same type or not null
  Iif (typeof v1 !== typeof v2 || v1 === null || v2 === null) {
    return false;
  }
 
  if (isNumberType(v1) && isNumberType(v2)) {
    return areNumbersEqualWithTolerance(v1, v2, tolerance);
  }
 
  Eif (isNumberArrayLike(v1) && isNumberArrayLike(v2)) {
    return areArraysEqual(v1, v2, tolerance);
  }
 
  return false;
}
 
const negative = (v) =>
  typeof v === 'number' ? -v : v?.map ? v.map(negative) : !v;
 
const abs = (v) =>
  typeof v === 'number' ? Math.abs(v) : v?.map ? v.map(abs) : v;
 
/**
 *  Compare negative values of both single numbers and vectors
 */
export const isEqualNegative = <ValueType>(
  v1: ValueType,
  v2: ValueType,
  tolerance = undefined
) => isEqual(v1, negative(v2) as unknown as ValueType, tolerance);
 
/**
 * Compare absolute values for single numbers and vectors.
 * Not recommended for large vectors as this creates a copy
 */
export const isEqualAbs = <ValueType>(
  v1: ValueType,
  v2: ValueType,
  tolerance = undefined
) => isEqual(abs(v1), abs(v2) as unknown as ValueType, tolerance);
 
/**
 * @param n - array of numbers or a simple number
 * @returns True if n or the first element of n is finite and not NaN
 */
export function isNumber(n: number[] | number): boolean {
  if (Array.isArray(n)) {
    return isNumber(n[0]);
  }
  return isFinite(n) && !isNaN(n);
}
 
export default isEqual;