1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {isString} from './object_utils'; 16 17export type ComparisonFn<X> = (a: X, b: X) => number; 18 19export type SortDirection = 'DESC' | 'ASC'; 20 21// Having a comparison function of type S and a getter that returns value of 22// type S from value of type T, values of type T can be compared. 23export function comparingBy<T, S>( 24 getter: (t: T) => S, 25 comparison: ComparisonFn<S>, 26): ComparisonFn<T> { 27 return (x, y) => { 28 return comparison(getter(x), getter(y)); 29 }; 30} 31 32export function withDirection<T>( 33 comparison: ComparisonFn<T>, 34 sortDirection?: SortDirection, 35): ComparisonFn<T> { 36 if (sortDirection !== 'DESC') { 37 return comparison; 38 } 39 40 return (x, y) => { 41 return comparison(y, x); 42 }; 43} 44 45export type SortableValue = 46 | string 47 | number 48 | bigint 49 | null 50 | Uint8Array 51 | undefined; 52 53function columnTypeKind(a: SortableValue): number { 54 if (a === undefined) { 55 return 0; 56 } 57 if (a === null) { 58 return 1; 59 } 60 if (typeof a === 'number') { 61 return 2; 62 } 63 if (isString(a)) { 64 return 3; 65 } 66 // a instanceof Uint8Array 67 return 4; 68} 69 70export function compareUniversal(a: SortableValue, b: SortableValue): number { 71 if (a === undefined && b === undefined) { 72 return 0; 73 } 74 if (a === null && b === null) { 75 return 0; 76 } 77 if (typeof a === 'number' && typeof b === 'number') { 78 return a - b; 79 } 80 if (isString(a) && isString(b)) { 81 return a.localeCompare(b); 82 } 83 if (a instanceof Uint8Array && b instanceof Uint8Array) { 84 // Do the lexicographical comparison 85 for (let i = 0; i < a.length && i < b.length; i++) { 86 if (a[i] < b[i]) { 87 return -1; 88 } 89 if (a[i] > b[i]) { 90 return 1; 91 } 92 } 93 // No discrepancies found in the common prefix, compare lengths of arrays. 94 return a.length - b.length; 95 } 96 97 // Values are of different kinds, compare the kinds 98 return columnTypeKind(a) - columnTypeKind(b); 99} 100