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.
- Of course, it is completely valid to return a Promise as your result value, and structure your code like
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 thematchValue
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 withmatchValue
, by not needing to have it in the same lexical scope.
- If a function is specified, it will be called with the original
- A
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.