UNPKG

locutus

Version:

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

60 lines (59 loc) 2.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bsearch = bsearch; function bsearch(values, callback) { // discuss at: https://locutus.io/ruby/Array/bsearch/ // 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: In numeric mode, positive means search right, negative means search left, and zero means match. // example 1: bsearch([1, 3, 4, 6, 8], (value) => value >= 4) // returns 1: 4 // example 2: bsearch([1, 3, 4, 6, 8], (value) => (value === 4 ? 0 : 4 > value ? 1 : -1)) // returns 2: 4 // example 3: bsearch([1, 3, 4, 6, 8], (value) => (value === 5 ? 0 : 5 > value ? 1 : -1)) // returns 3: null if (typeof callback !== 'function') { throw new TypeError('bsearch(): 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(): callback must consistently return booleans or numbers'); } mode = 'numeric'; if (result === 0) { return value; } if (result > 0) { low = mid + 1; } else { high = mid - 1; } continue; } if (mode === 'numeric') { throw new TypeError('bsearch(): callback must consistently return booleans or numbers'); } mode = 'boolean'; if (result) { candidateIndex = mid; high = mid - 1; } else { low = mid + 1; } } return candidateIndex === null ? null : values[candidateIndex]; }