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

Polishs arraysFind #192156

Merged
merged 1 commit into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 2 additions & 114 deletions src/vs/base/common/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationError } from 'vs/base/common/errors';
import { ISplice } from 'vs/base/common/sequence';
import { findFirstIdxMonotonousOrArrLen } from './arraysFind';

/**
* Returns the last element of an array.
Expand Down Expand Up @@ -106,27 +107,6 @@ export function binarySearch2(length: number, compareToKey: (index: number) => n
return -(low + 1);
}

/**
* Takes a sorted array and a function p. The array is sorted in such a way that all elements where p(x) is false
* are located before all elements where p(x) is true.
* @returns the least x for which p(x) is true or array.length if no element fullfills the given function.
*/
export function findFirstInSorted<T>(array: ReadonlyArray<T>, p: (x: T) => boolean): number {
let low = 0, high = array.length;
if (high === 0) {
return 0; // no children
}
while (low < high) {
const mid = Math.floor((low + high) / 2);
if (p(array[mid])) {
high = mid;
} else {
low = mid + 1;
}
}
return low;
}

type Compare<T> = (a: T, b: T) => number;


Expand Down Expand Up @@ -345,7 +325,7 @@ function topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, re
const element = array[i];
if (compare(element, result[n - 1]) < 0) {
result.pop();
const j = findFirstInSorted(result, e => compare(element, e) < 0);
const j = findFirstIdxMonotonousOrArrLen(result, e => compare(element, e) < 0);
result.splice(j, 0, element);
}
}
Expand Down Expand Up @@ -427,26 +407,6 @@ export function uniqueFilter<T, R>(keyFn: (t: T) => R): (t: T) => boolean {
};
}

export function findLast<T>(arr: readonly T[], predicate: (item: T) => boolean): T | undefined {
const idx = findLastIndex(arr, predicate);
if (idx === -1) {
return undefined;
}
return arr[idx];
}

export function findLastIndex<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean): number {
for (let i = array.length - 1; i >= 0; i--) {
const element = array[i];

if (fn(element)) {
return i;
}
}

return -1;
}

export function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue: NotFound): T | NotFound;
export function firstOrDefault<T>(array: ReadonlyArray<T>): T | undefined;
export function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue?: NotFound): T | NotFound | undefined {
Expand Down Expand Up @@ -622,20 +582,6 @@ export function getRandomElement<T>(arr: T[]): T | undefined {
return arr[Math.floor(Math.random() * arr.length)];
}

/**
* Returns the first mapped value of the array which is not undefined.
*/
export function mapFind<T, R>(array: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
for (const value of array) {
const mapped = mapFn(value);
if (mapped !== undefined) {
return mapped;
}
}

return undefined;
}

/**
* Insert the new items in the array.
* @param array The original array.
Expand Down Expand Up @@ -747,64 +693,6 @@ export function reverseOrder<TItem>(comparator: Comparator<TItem>): Comparator<T
return (a, b) => -comparator(a, b);
}

/**
* Returns the first item that is equal to or greater than every other item.
*/
export function findMaxBy<T>(items: readonly T[], comparator: Comparator<T>): T | undefined {
if (items.length === 0) {
return undefined;
}

let max = items[0];
for (let i = 1; i < items.length; i++) {
const item = items[i];
if (comparator(item, max) > 0) {
max = item;
}
}
return max;
}

/**
* Returns the last item that is equal to or greater than every other item.
*/
export function findLastMaxBy<T>(items: readonly T[], comparator: Comparator<T>): T | undefined {
if (items.length === 0) {
return undefined;
}

let max = items[0];
for (let i = 1; i < items.length; i++) {
const item = items[i];
if (comparator(item, max) >= 0) {
max = item;
}
}
return max;
}

/**
* Returns the first item that is equal to or less than every other item.
*/
export function findMinBy<T>(items: readonly T[], comparator: Comparator<T>): T | undefined {
return findMaxBy(items, (a, b) => -comparator(a, b));
}

export function findMaxIdxBy<T>(items: readonly T[], comparator: Comparator<T>): number {
if (items.length === 0) {
return -1;
}

let maxIdx = 0;
for (let i = 1; i < items.length; i++) {
const item = items[i];
if (comparator(item, items[maxIdx]) > 0) {
maxIdx = i;
}
}
return maxIdx;
}

export class ArrayQueue<T> {
private firstIdx = 0;
private lastIdx = this.items.length - 1;
Expand Down
129 changes: 111 additions & 18 deletions src/vs/base/common/arraysFind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,37 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Comparator } from './arrays';

export function findLast<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
const idx = findLastIdx(array, predicate);
if (idx === -1) {
return undefined;
}
return array[idx];
}

export function findLastIdx<T>(array: readonly T[], predicate: (item: T) => boolean): number {
for (let i = array.length - 1; i >= 0; i--) {
const element = array[i];

if (predicate(element)) {
return i;
}
}

return -1;
}

/**
* Finds the last item where predicate is true using binary search.
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
*
* @returns `undefined` if no item matches, otherwise the last item that matches the predicate.
*/
export function findLastMonotonous<T>(arr: T[], predicate: (item: T) => boolean): T | undefined {
const idx = findLastIdxMonotonous(arr, predicate);
return idx === -1 ? undefined : arr[idx];
export function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
const idx = findLastIdxMonotonous(array, predicate);
return idx === -1 ? undefined : array[idx];
}

/**
Expand All @@ -20,12 +42,12 @@ export function findLastMonotonous<T>(arr: T[], predicate: (item: T) => boolean)
*
* @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
*/
export function findLastIdxMonotonous<T>(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number {
export function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
let i = startIdx;
let j = endIdxEx;
while (i < j) {
const k = Math.floor((i + j) / 2);
if (predicate(arr[k])) {
if (predicate(array[k])) {
i = k + 1;
} else {
j = k;
Expand All @@ -34,16 +56,15 @@ export function findLastIdxMonotonous<T>(arr: T[], predicate: (item: T) => boole
return i - 1;
}


/**
* Finds the first item where predicate is true using binary search.
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
*
* @returns `undefined` if no item matches, otherwise the first item that matches the predicate.
*/
export function findFirstMonotonous<T>(arr: T[], predicate: (item: T) => boolean): T | undefined {
const idx = findFirstIdxMonotonousOrArrLen(arr, predicate);
return idx === arr.length ? undefined : arr[idx];
export function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
return idx === array.length ? undefined : array[idx];
}

/**
Expand All @@ -52,12 +73,12 @@ export function findFirstMonotonous<T>(arr: T[], predicate: (item: T) => boolean
*
* @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
*/
export function findFirstIdxMonotonousOrArrLen<T>(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number {
export function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
let i = startIdx;
let j = endIdxEx;
while (i < j) {
const k = Math.floor((i + j) / 2);
if (predicate(arr[k])) {
if (predicate(array[k])) {
j = k;
} else {
i = k + 1;
Expand All @@ -66,9 +87,9 @@ export function findFirstIdxMonotonousOrArrLen<T>(arr: T[], predicate: (item: T)
return i;
}

export function findFirstIdxMonotonous<T>(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number {
const idx = findFirstIdxMonotonousOrArrLen(arr, predicate, startIdx, endIdxEx);
return idx === arr.length ? -1 : idx;
export function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);
return idx === array.length ? -1 : idx;
}

/**
Expand All @@ -83,7 +104,7 @@ export class MonotonousArray<T> {
private _findLastMonotonousLastIdx = 0;
private _prevFindLastPredicate: ((item: T) => boolean) | undefined;

constructor(private readonly _items: T[]) {
constructor(private readonly _array: readonly T[]) {
}

/**
Expand All @@ -93,7 +114,7 @@ export class MonotonousArray<T> {
findLastMonotonous(predicate: (item: T) => boolean): T | undefined {
if (MonotonousArray.assertInvariants) {
if (this._prevFindLastPredicate) {
for (const item of this._items) {
for (const item of this._array) {
if (this._prevFindLastPredicate(item) && !predicate(item)) {
throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.');
}
Expand All @@ -102,8 +123,80 @@ export class MonotonousArray<T> {
this._prevFindLastPredicate = predicate;
}

const idx = findLastIdxMonotonous(this._items, predicate, this._findLastMonotonousLastIdx);
const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);
this._findLastMonotonousLastIdx = idx + 1;
return idx === -1 ? undefined : this._items[idx];
return idx === -1 ? undefined : this._array[idx];
}
}

/**
* Returns the first item that is equal to or greater than every other item.
*/
export function findFirstMaxBy<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
if (array.length === 0) {
return undefined;
}

let max = array[0];
for (let i = 1; i < array.length; i++) {
const item = array[i];
if (comparator(item, max) > 0) {
max = item;
}
}
return max;
}

/**
* Returns the last item that is equal to or greater than every other item.
*/
export function findLastMaxBy<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
if (array.length === 0) {
return undefined;
}

let max = array[0];
for (let i = 1; i < array.length; i++) {
const item = array[i];
if (comparator(item, max) >= 0) {
max = item;
}
}
return max;
}

/**
* Returns the first item that is equal to or less than every other item.
*/
export function findFirstMinBy<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
return findFirstMaxBy(array, (a, b) => -comparator(a, b));
}

export function findMaxIdxBy<T>(array: readonly T[], comparator: Comparator<T>): number {
if (array.length === 0) {
return -1;
}

let maxIdx = 0;
for (let i = 1; i < array.length; i++) {
const item = array[i];
if (comparator(item, array[maxIdx]) > 0) {
maxIdx = i;
}
}
return maxIdx;
}

/**
* Returns the first mapped value of the array which is not undefined.
*/
export function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
for (const value of items) {
const mapped = mapFn(value);
if (mapped !== undefined) {
return mapped;
}
}

return undefined;
}