UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

60 lines (59 loc) 2.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bsearch_index = bsearch_index; function bsearch_index(values, callback) { // discuss at: https://locutus.io/ruby/Array/bsearch_index/ // parity verified: Ruby 3.3 // original by: Kevin van Zonneveld (https://kvz.io) // note 1: Supports Ruby's boolean "find minimum" mode and numeric "find any exact match" mode. // note 2: Returns the matching index instead of the matching element. // example 1: bsearch_index([1, 3, 4, 6, 8], (value) => value >= 4) // returns 1: 2 // example 2: bsearch_index([1, 3, 4, 6, 8], (value) => (value === 4 ? 0 : 4 > value ? 1 : -1)) // returns 2: 2 // example 3: bsearch_index([1, 3, 4, 6, 8], (value) => (value === 5 ? 0 : 5 > value ? 1 : -1)) // returns 3: null if (typeof callback !== 'function') { throw new TypeError('bsearch_index(): callback must be a function'); } if (!Array.isArray(values) || values.length === 0) { return null; } let low = 0; let high = values.length - 1; let candidateIndex = null; let mode = null; while (low <= high) { const mid = low + Math.floor((high - low) / 2); const value = values[mid]; const result = callback(value, mid, values); if (typeof result === 'number') { if (mode === 'boolean') { throw new TypeError('bsearch_index(): callback must consistently return booleans or numbers'); } mode = 'numeric'; if (result === 0) { return mid; } if (result > 0) { low = mid + 1; } else { high = mid - 1; } continue; } if (mode === 'numeric') { throw new TypeError('bsearch_index(): callback must consistently return booleans or numbers'); } mode = 'boolean'; if (result) { candidateIndex = mid; high = mid - 1; } else { low = mid + 1; } } return candidateIndex; }