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

match-value

v1.1.0

Published

Utility for mapping values to other values or behaviour, a bit like a match statement

Downloads

9

Readme

match-value

Since JS as a language does not (yet) have a match statement or expression, this package implements the same concept for the most common cases.

You specify a mapping of source values to result values, as an object, and match-value maps from one to the other, throwing an error if an invalid source value is specified, optionally having a catch-all for unknown values, and with support for lazily-produced result values.

In other words, it turns this:

let mode;

if (flag === "r") {
	mode = "readOnly";
} else if (flag === "rw") {
	mode = "readWrite";
} else if (flag === "a") {
	mode = "appendOnly";
} else {
	mode = `unknown:${someExpensiveCall(flag)}`;
}

... or this:

let mode;

switch (flag) {
	case "r":
		mode = "readOnly";
		break;
	case "rw":
		mode = "readWrite";
		break;
	case "a":
		mode = "appendOnly";
		break;
	default:
		mode = `unknown:${someExpensiveCall(flag)}`;
		break;
}

... into this:

const matchValue = require("match-value");

let mode = matchValue(flag, {
	r: "readOnly",
	rw: "readWrite",
	a: "appendOnly",
	_: () => `unknown:${someExpensiveCall(flag)}`
});

Less repetition, and a much clearer intention.

Limitations

  • Source values may only be strings. Any input that isn't a string will always throw an error (or hit the catch-all, if you've specified one).
    • This is unfortunately to do with a limitation of JS as a language, that makes it impossible to ergonomically specify mappings where the source value isn't a string key. If JS ever gets Map literals, this limitation can be fixed.
  • Source values may only be literal values. There's no type/shape matching, like you might be used to if you've used a match construct in another language.
    • Again, this is due to a language limitation.
  • This library is completely unaware of the existence of Promises or asynchronous functions; all results are expected to be produced synchronously.
    • Of course, it is completely valid to return a Promise as your result value, and structure your code like Promise.resolve(matchValue(input, { ... }) to consistently get a Promise out of it.

License, donations, and other boilerplate

Licensed under either the WTFPL or CC0, at your choice. In practice, that means it's more or less public domain, and you can do whatever you want with it. Giving credit is not required, but still very much appreciated! I'd love to hear from you if this module was useful to you.

Creating and maintaining open-source modules is a lot of work. A donation is also not required, but much appreciated! You can donate here.

Example

A full version of the example above:

"use strict";

const matchValue = require("match-value");

let flag = "a"; // Hardcoded for example

let mode = matchValue(flag, {
	r: "readOnly",
	rw: "readWrite",
	a: "appendOnly",
	_: () => `unknown:${someExpensiveCall(flag)}`
});

console.log(mode); // appendOnly

API

matchValue(input, mapping)

Converts the input to a result based on the mapping.

  • input: The value to convert.
  • mapping: An object that describes the mapping in the format { possibleInput: result }.
    • A possibleInput of _ defines a "catch-all" case; anything that doesn't match any of the other possible inputs, will trigger the catch-all case.
    • The result may be either a literal value, or a function that produces that value. If it's a function, the function will only be called when there is a match, and the return value of that call will be returned from the matchValue call. If you want to return functions rather than call them, see the .literal method below.
      • If a function is specified, it will be called with the original input as its first and only argument; this makes it easier to specify the mapping object in a different place from where it is used with matchValue, by not needing to have it in the same lexical scope.

Returns a result if a match was found, or the catch-all was hit. Throws an Error if no match was found and no catch-all was specified.

To throw a custom error type instead, define a catch-all function that throws that error, like so:

matchValue(input, {
	someKey: "someResult",
	someOtherKey: "someOtherResult",
	_: () => { throw new CustomError("Oh no!"); }
});

Note that if you're using an arrow function here, it must have curly braces around the throw; since a throw in JS is a statement, not an expression, it may not appear in a brace-less arrow function (which only accepts expressions).

matchValue.literal(input, mapping)

Exactly like matchValue above, but if the result value is a function, it will be returned as-is, rather than being called; that is, the function is the result.

Note that this also means that a custom error-throwing handler is not possible in this case.

Changelog

v1.1.0 (June 3, 2020)

  • Feature: A lazy function will now receive the original input value as its first and only argument.

v1.0.1 (June 3, 2020)

  • Bugfix: Fixed function handling; they were interpreted as literals in non-literal mode and vice versa. It now works correctly, according to what the documentation specifies.

v1.0.0 (March 23, 2020)

Initial release.