npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nano-binary-search

v1.0.6

Published

Binary search for JavaScript done right.

Downloads

98

Readme

nano-binary-search NPM version

This is a nano binary search implementation. It is a tiny single file with no dependencies. The only reason I wrote it because I wrote it countless times before, I think it is perfect now because it is done right and fits JavaScript — it is ripe for code reuse.

For TypeScript users the typings are included.

Why?

Why do I think it is done right? Because it supports important invariants with splice().

No need to worry about inserting values

import binarySearch from 'nano-binary-search';

const sortedArray = [];

for (let i = 0; i < 100; ++i) {
  const value = Math.floor(Math.random() * 1000);

  // THIS IS THE IMPORTANT INVARIANT:
  // - works with `splice()` seamlessly to add values
  const index = binarySearch(sortedArray, x => x < value);
  sortedArray.splice(index, 0, value);
  // `sortedArray` is always sorted
}

What if the array is empty? That's fine. What if the value is outside the range of the array? That's fine too.

No need to worry about removing values

import binarySearch from 'nano-binary-search';

const sortedArray = [1, 3, 3, 4];

// THIS IS THE IMPORTANT INVARIANT:
// - works with `splice()` seamlessly to remove equal values
const lowerIndex = binarySearch(sortedArray, x => x < 3),
  upperIndex = binarySearch(sortedArray, x => x <= 3, lowerIndex);
sortedArray.splice(lowerIndex, upperIndex - lowerIndex);
// again, `sortedArray` is always sorted

What if there is no such value in the array? That's fine. It still works.

API that makes sense

There is no need to pass in a function and a comparison value every time. In the modern JavaScript/TypeScript, it is easier to get the comparison value straight from the closure like in the examples.

Do you want to search a sub-array? Just pass in indices.

API

The TypeScript-like API is as follows:

const index: number = binarySearch<T>(
  sortedArray: readonly T[],
  lessFn: (value: T, index: number, array: readonly T[]) => boolean,
  l: number = 0,
  r: number = sortedArray.length
): boolean;
  • Inputs:
    • sortedArray — sorted array of some values. We don't care about values in this array. It is up to lessFn to compare them. The array should be sorted in a compatible way with lessFn.
    • lessFn — function that takes three argument and returns a truthy value if the first argument (a value from array) is less than our value, whatever it is. The second value is its index, and the third is the sortedArray.
      • The function interface is modeled on the callback function of array methods.
    • l — left index. This index is inclusive. Defaults to 0.
    • r — right index. This index is exclusive. Defaults to sortedArray.length.

The function return an index, where we can safely insert the searched value with splice():

  • if we used < operator as the comparison function, the index will point to the first value that is greater or equal than the searched value.
  • if we used <= operator as the comparison function, the index will point to the first value that is greater than the searched value.

That's all Folks!

Q & A

Is it fast?

Yes.

The only reasonable way to make it faster is to take its code and inline lessFn(). The other idea is to inline binarySearch() itself in your code. That's about all.

What if I want to take into account the index of the searched value?

That's why lessFn(value, index, array) has extra arguments.

What if the array uses a custom comparator for sorting, while this binary search uses a less function?

Simple:

let compareFn; // some complex function defined elsewhere
sortedArray.sort(compareFn);

let value; // some search value defined elsewhere

const lessFn = x => compareFn(x, value) < 0,
  index = binarySearch(sortedArray, lessFn);

Why doesn't it use a comparator function for searching?

We don't need to compare values in the array for equality. A simple less function is enough. In many cases it is easier to implement just a less function.

For example (two argument version for simplicity):

const stringLessFn = (a, b) => a < b;

// comparator #1 (two comparisons)
const stringCompareFn1 = (a, b) => a < b ? -1 : a > b ? 1 : 0;

// comparator #2: smarter (a method call)
const stringCompareFn2 = (a, b) => a.localeCompare(b);

I still have questions!

Look at the code of index.js and tests/ for more details. Go to the GitHub repository and ask.

License

This project is licensed under the BSD-3-Clause license.

Release history

  • 1.0.6 Updated deps
  • 1.0.5 Updated deps
  • 1.0.4 Updated deps
  • 1.0.3 Added a reference to the TS types
  • 1.0.2 Improved docs
  • 1.0.1 Added TS typings
  • 1.0.0 Initial release