Path: blob/main/extensions/copilot/src/util/vs/base/common/arraysFind.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import { Comparator } from './arrays';89export function findLast<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;10export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;11export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): T | undefined {12const idx = findLastIdx(array, predicate, fromIndex);13if (idx === -1) {14return undefined;15}16return array[idx];17}1819export function findLastIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): number {20for (let i = fromIndex; i >= 0; i--) {21const element = array[i];2223if (predicate(element, i)) {24return i;25}26}2728return -1;29}3031export function findFirst<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;32export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;33export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): T | undefined {34const idx = findFirstIdx(array, predicate, fromIndex);35if (idx === -1) {36return undefined;37}38return array[idx];39}4041export function findFirstIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): number {42for (let i = fromIndex; i < array.length; i++) {43const element = array[i];4445if (predicate(element, i)) {46return i;47}48}4950return -1;51}5253/**54* Finds the last item where predicate is true using binary search.55* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!56*57* @returns `undefined` if no item matches, otherwise the last item that matches the predicate.58*/59export function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {60const idx = findLastIdxMonotonous(array, predicate);61return idx === -1 ? undefined : array[idx];62}6364/**65* Finds the last item where predicate is true using binary search.66* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!67*68* @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.69*/70export function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {71let i = startIdx;72let j = endIdxEx;73while (i < j) {74const k = Math.floor((i + j) / 2);75if (predicate(array[k])) {76i = k + 1;77} else {78j = k;79}80}81return i - 1;82}8384/**85* Finds the first item where predicate is true using binary search.86* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!87*88* @returns `undefined` if no item matches, otherwise the first item that matches the predicate.89*/90export function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {91const idx = findFirstIdxMonotonousOrArrLen(array, predicate);92return idx === array.length ? undefined : array[idx];93}9495/**96* Finds the first item where predicate is true using binary search.97* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!98*99* @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.100*/101export function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {102let i = startIdx;103let j = endIdxEx;104while (i < j) {105const k = Math.floor((i + j) / 2);106if (predicate(array[k])) {107j = k;108} else {109i = k + 1;110}111}112return i;113}114115export function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {116const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);117return idx === array.length ? -1 : idx;118}119120/**121* Use this when122* * You have a sorted array123* * You query this array with a monotonous predicate to find the last item that has a certain property.124* * You query this array multiple times with monotonous predicates that get weaker and weaker.125*/126export class MonotonousArray<T> {127public static assertInvariants = false;128129private _findLastMonotonousLastIdx = 0;130private _prevFindLastPredicate: ((item: T) => boolean) | undefined;131132constructor(private readonly _array: readonly T[]) {133}134135/**136* The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!137* For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.138*/139findLastMonotonous(predicate: (item: T) => boolean): T | undefined {140if (MonotonousArray.assertInvariants) {141if (this._prevFindLastPredicate) {142for (const item of this._array) {143if (this._prevFindLastPredicate(item) && !predicate(item)) {144throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.');145}146}147}148this._prevFindLastPredicate = predicate;149}150151const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);152this._findLastMonotonousLastIdx = idx + 1;153return idx === -1 ? undefined : this._array[idx];154}155}156157/**158* Returns the first item that is equal to or greater than every other item.159*/160export function findFirstMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {161if (array.length === 0) {162return undefined;163}164165let max = array[0];166for (let i = 1; i < array.length; i++) {167const item = array[i];168if (comparator(item, max) > 0) {169max = item;170}171}172return max;173}174175/**176* Returns the last item that is equal to or greater than every other item.177*/178export function findLastMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {179if (array.length === 0) {180return undefined;181}182183let max = array[0];184for (let i = 1; i < array.length; i++) {185const item = array[i];186if (comparator(item, max) >= 0) {187max = item;188}189}190return max;191}192193/**194* Returns the first item that is equal to or less than every other item.195*/196export function findFirstMin<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {197return findFirstMax(array, (a, b) => -comparator(a, b));198}199200export function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): number {201if (array.length === 0) {202return -1;203}204205let maxIdx = 0;206for (let i = 1; i < array.length; i++) {207const item = array[i];208if (comparator(item, array[maxIdx]) > 0) {209maxIdx = i;210}211}212return maxIdx;213}214215/**216* Returns the first mapped value of the array which is not undefined.217*/218export function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {219for (const value of items) {220const mapped = mapFn(value);221if (mapped !== undefined) {222return mapped;223}224}225226return undefined;227}228229230