@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
130 lines (114 loc) • 4.81 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2023 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export namespace ArrayUtils {
export interface Head<T> extends Array<T> {
head(): T;
}
export interface Tail<T> extends Array<T> {
tail(): T;
}
export interface Children<T> extends Array<T> {
children(): Tail<T>
}
export const TailImpl = {
tail<T>(this: Array<T>): T {
return this[this.length - 1];
},
};
export const HeadAndChildrenImpl = {
head<T>(this: Array<T>): T {
return this[0];
},
children<T>(this: Array<T>): Tail<T> {
return Object.assign(this.slice(1), TailImpl);
}
};
export interface HeadAndTail<T> extends Head<T>, Tail<T>, Children<T> { }
export function asTail<T>(array: Array<T>): Tail<T> {
return Object.assign(array, TailImpl);
}
export function asHeadAndTail<T>(array: Array<T>): HeadAndTail<T> {
return Object.assign(array, HeadAndChildrenImpl, TailImpl);
}
export enum Sort {
LeftBeforeRight = -1,
RightBeforeLeft = 1,
Equal = 0,
}
// Copied from https://github.com/microsoft/vscode/blob/9c29becfad5f68270b9b23efeafb147722c5feba/src/vs/base/common/arrays.ts
/**
* Performs a binary search algorithm over a sorted collection. Useful for cases
* when we need to perform a binary search over something that isn't actually an
* array, and converting data to an array would defeat the use of binary search
* in the first place.
*
* @param length The collection length.
* @param compareToKey A function that takes an index of an element in the
* collection and returns zero if the value at this index is equal to the
* search key, a negative number if the value precedes the search key in the
* sorting order, or a positive number if the search key precedes the value.
* @return A non-negative index of an element, if found. If not found, the
* result is -(n+1) (or ~n, using bitwise notation), where n is the index
* where the key should be inserted to maintain the sorting order.
*/
export function binarySearch2(length: number, compareToKey: (index: number) => number): number {
let low = 0;
let high = length - 1;
while (low <= high) {
const mid = ((low + high) / 2) | 0;
const comp = compareToKey(mid);
if (comp < 0) {
low = mid + 1;
} else if (comp > 0) {
high = mid - 1;
} else {
return mid;
}
}
return -(low + 1);
}
export function partition<T>(array: T[], filter: (e: T, idx: number, arr: T[]) => boolean | undefined): [T[], T[]] {
const pass: T[] = [];
const fail: T[] = [];
array.forEach((e, idx, arr) => (filter(e, idx, arr) ? pass : fail).push(e));
return [pass, fail];
}
/**
* @returns New array with all falsy values removed. The original array IS NOT modified.
*/
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
return <T[]>array.filter(e => !!e);
}
/**
* groups array elements through a comparator function
* @param data array of elements to group
* @param compare comparator function: return of 0 means should group, anything above means not group
* @returns array of arrays with grouped elements
*/
export function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {
const result: T[][] = [];
let currentGroup: T[] | undefined = undefined;
for (const element of data.slice(0).sort(compare)) {
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
currentGroup = [element];
result.push(currentGroup);
} else {
currentGroup.push(element);
}
}
return result;
}
}