| // Copyright (C) 2023 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import {isString} from './object_utils'; |
| |
| export type ComparisonFn<X> = (a: X, b: X) => number; |
| |
| export type SortDirection = 'DESC' | 'ASC'; |
| |
| // Having a comparison function of type S and a getter that returns value of |
| // type S from value of type T, values of type T can be compared. |
| export function comparingBy<T, S>( |
| getter: (t: T) => S, |
| comparison: ComparisonFn<S>, |
| ): ComparisonFn<T> { |
| return (x, y) => { |
| return comparison(getter(x), getter(y)); |
| }; |
| } |
| |
| export function withDirection<T>( |
| comparison: ComparisonFn<T>, |
| sortDirection?: SortDirection, |
| ): ComparisonFn<T> { |
| if (sortDirection !== 'DESC') { |
| return comparison; |
| } |
| |
| return (x, y) => { |
| return comparison(y, x); |
| }; |
| } |
| |
| export type SortableValue = |
| | string |
| | number |
| | bigint |
| | null |
| | Uint8Array |
| | undefined; |
| |
| function columnTypeKind(a: SortableValue): number { |
| if (a === undefined) { |
| return 0; |
| } |
| if (a === null) { |
| return 1; |
| } |
| if (typeof a === 'number') { |
| return 2; |
| } |
| if (isString(a)) { |
| return 3; |
| } |
| // a instanceof Uint8Array |
| return 4; |
| } |
| |
| export function compareUniversal(a: SortableValue, b: SortableValue): number { |
| if (a === undefined && b === undefined) { |
| return 0; |
| } |
| if (a === null && b === null) { |
| return 0; |
| } |
| if (typeof a === 'number' && typeof b === 'number') { |
| return a - b; |
| } |
| if (isString(a) && isString(b)) { |
| return a.localeCompare(b); |
| } |
| if (a instanceof Uint8Array && b instanceof Uint8Array) { |
| // Do the lexicographical comparison |
| for (let i = 0; i < a.length && i < b.length; i++) { |
| if (a[i] < b[i]) { |
| return -1; |
| } |
| if (a[i] > b[i]) { |
| return 1; |
| } |
| } |
| // No discrepancies found in the common prefix, compare lengths of arrays. |
| return a.length - b.length; |
| } |
| |
| // Values are of different kinds, compare the kinds |
| return columnTypeKind(a) - columnTypeKind(b); |
| } |