Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/arraysFind.ts
3291 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Comparator } from './arrays.js';
7
8
export function findLast<T, R extends T>(array: readonly T[], predicate: (item: T) => item is R, fromIndex?: number): R | undefined;
9
export function findLast<T>(array: readonly T[], predicate: (item: T) => unknown, fromIndex?: number): T | undefined;
10
export function findLast<T>(array: readonly T[], predicate: (item: T) => unknown, fromIndex = array.length - 1): T | undefined {
11
const idx = findLastIdx(array, predicate, fromIndex);
12
if (idx === -1) {
13
return undefined;
14
}
15
return array[idx];
16
}
17
18
export function findLastIdx<T>(array: readonly T[], predicate: (item: T) => unknown, fromIndex = array.length - 1): number {
19
for (let i = fromIndex; i >= 0; i--) {
20
const element = array[i];
21
22
if (predicate(element)) {
23
return i;
24
}
25
}
26
27
return -1;
28
}
29
30
/**
31
* Finds the last item where predicate is true using binary search.
32
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
33
*
34
* @returns `undefined` if no item matches, otherwise the last item that matches the predicate.
35
*/
36
export function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
37
const idx = findLastIdxMonotonous(array, predicate);
38
return idx === -1 ? undefined : array[idx];
39
}
40
41
/**
42
* Finds the last item where predicate is true using binary search.
43
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
44
*
45
* @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
46
*/
47
export function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
48
let i = startIdx;
49
let j = endIdxEx;
50
while (i < j) {
51
const k = Math.floor((i + j) / 2);
52
if (predicate(array[k])) {
53
i = k + 1;
54
} else {
55
j = k;
56
}
57
}
58
return i - 1;
59
}
60
61
/**
62
* Finds the first item where predicate is true using binary search.
63
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
64
*
65
* @returns `undefined` if no item matches, otherwise the first item that matches the predicate.
66
*/
67
export function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
68
const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
69
return idx === array.length ? undefined : array[idx];
70
}
71
72
/**
73
* Finds the first item where predicate is true using binary search.
74
* `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
75
*
76
* @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
77
*/
78
export function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
79
let i = startIdx;
80
let j = endIdxEx;
81
while (i < j) {
82
const k = Math.floor((i + j) / 2);
83
if (predicate(array[k])) {
84
j = k;
85
} else {
86
i = k + 1;
87
}
88
}
89
return i;
90
}
91
92
export function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
93
const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);
94
return idx === array.length ? -1 : idx;
95
}
96
97
/**
98
* Use this when
99
* * You have a sorted array
100
* * You query this array with a monotonous predicate to find the last item that has a certain property.
101
* * You query this array multiple times with monotonous predicates that get weaker and weaker.
102
*/
103
export class MonotonousArray<T> {
104
public static assertInvariants = false;
105
106
private _findLastMonotonousLastIdx = 0;
107
private _prevFindLastPredicate: ((item: T) => boolean) | undefined;
108
109
constructor(private readonly _array: readonly T[]) {
110
}
111
112
/**
113
* The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
114
* For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.
115
*/
116
findLastMonotonous(predicate: (item: T) => boolean): T | undefined {
117
if (MonotonousArray.assertInvariants) {
118
if (this._prevFindLastPredicate) {
119
for (const item of this._array) {
120
if (this._prevFindLastPredicate(item) && !predicate(item)) {
121
throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.');
122
}
123
}
124
}
125
this._prevFindLastPredicate = predicate;
126
}
127
128
const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);
129
this._findLastMonotonousLastIdx = idx + 1;
130
return idx === -1 ? undefined : this._array[idx];
131
}
132
}
133
134
/**
135
* Returns the first item that is equal to or greater than every other item.
136
*/
137
export function findFirstMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
138
if (array.length === 0) {
139
return undefined;
140
}
141
142
let max = array[0];
143
for (let i = 1; i < array.length; i++) {
144
const item = array[i];
145
if (comparator(item, max) > 0) {
146
max = item;
147
}
148
}
149
return max;
150
}
151
152
/**
153
* Returns the last item that is equal to or greater than every other item.
154
*/
155
export function findLastMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
156
if (array.length === 0) {
157
return undefined;
158
}
159
160
let max = array[0];
161
for (let i = 1; i < array.length; i++) {
162
const item = array[i];
163
if (comparator(item, max) >= 0) {
164
max = item;
165
}
166
}
167
return max;
168
}
169
170
/**
171
* Returns the first item that is equal to or less than every other item.
172
*/
173
export function findFirstMin<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
174
return findFirstMax(array, (a, b) => -comparator(a, b));
175
}
176
177
export function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): number {
178
if (array.length === 0) {
179
return -1;
180
}
181
182
let maxIdx = 0;
183
for (let i = 1; i < array.length; i++) {
184
const item = array[i];
185
if (comparator(item, array[maxIdx]) > 0) {
186
maxIdx = i;
187
}
188
}
189
return maxIdx;
190
}
191
192
/**
193
* Returns the first mapped value of the array which is not undefined.
194
*/
195
export function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
196
for (const value of items) {
197
const mapped = mapFn(value);
198
if (mapped !== undefined) {
199
return mapped;
200
}
201
}
202
203
return undefined;
204
}
205
206