mirror of
https://github.com/neocities/neocities.git
synced 2025-05-02 04:58:01 +02:00
175 lines
5.9 KiB
JavaScript
175 lines
5.9 KiB
JavaScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
export function findLast(array, predicate) {
|
|
const idx = findLastIdx(array, predicate);
|
|
if (idx === -1) {
|
|
return undefined;
|
|
}
|
|
return array[idx];
|
|
}
|
|
export function findLastIdx(array, predicate, fromIndex = array.length - 1) {
|
|
for (let i = fromIndex; 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(array, predicate) {
|
|
const idx = findLastIdxMonotonous(array, predicate);
|
|
return idx === -1 ? undefined : array[idx];
|
|
}
|
|
/**
|
|
* 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 `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
|
|
*/
|
|
export function findLastIdxMonotonous(array, predicate, startIdx = 0, endIdxEx = array.length) {
|
|
let i = startIdx;
|
|
let j = endIdxEx;
|
|
while (i < j) {
|
|
const k = Math.floor((i + j) / 2);
|
|
if (predicate(array[k])) {
|
|
i = k + 1;
|
|
}
|
|
else {
|
|
j = k;
|
|
}
|
|
}
|
|
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(array, predicate) {
|
|
const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
|
|
return idx === array.length ? undefined : array[idx];
|
|
}
|
|
/**
|
|
* 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 `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
|
|
*/
|
|
export function findFirstIdxMonotonousOrArrLen(array, predicate, startIdx = 0, endIdxEx = array.length) {
|
|
let i = startIdx;
|
|
let j = endIdxEx;
|
|
while (i < j) {
|
|
const k = Math.floor((i + j) / 2);
|
|
if (predicate(array[k])) {
|
|
j = k;
|
|
}
|
|
else {
|
|
i = k + 1;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
/**
|
|
* Use this when
|
|
* * You have a sorted array
|
|
* * You query this array with a monotonous predicate to find the last item that has a certain property.
|
|
* * You query this array multiple times with monotonous predicates that get weaker and weaker.
|
|
*/
|
|
export class MonotonousArray {
|
|
static { this.assertInvariants = false; }
|
|
constructor(_array) {
|
|
this._array = _array;
|
|
this._findLastMonotonousLastIdx = 0;
|
|
}
|
|
/**
|
|
* The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
|
|
* For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.
|
|
*/
|
|
findLastMonotonous(predicate) {
|
|
if (MonotonousArray.assertInvariants) {
|
|
if (this._prevFindLastPredicate) {
|
|
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.');
|
|
}
|
|
}
|
|
}
|
|
this._prevFindLastPredicate = predicate;
|
|
}
|
|
const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);
|
|
this._findLastMonotonousLastIdx = idx + 1;
|
|
return idx === -1 ? undefined : this._array[idx];
|
|
}
|
|
}
|
|
/**
|
|
* Returns the first item that is equal to or greater than every other item.
|
|
*/
|
|
export function findFirstMax(array, comparator) {
|
|
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 findLastMax(array, comparator) {
|
|
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 findFirstMin(array, comparator) {
|
|
return findFirstMax(array, (a, b) => -comparator(a, b));
|
|
}
|
|
export function findMaxIdx(array, comparator) {
|
|
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(items, mapFn) {
|
|
for (const value of items) {
|
|
const mapped = mapFn(value);
|
|
if (mapped !== undefined) {
|
|
return mapped;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|