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

fp-multik

v0.1.3

Published

JS/TS lightweight value-multimethod util

Downloads

4

Readme

fp-multik 🤹🏼‍♂️

npm version

Small functional utility for control flow and conditional operator for functions. Multik is value-based multimethod for Javascript/Typescript programs.

It's simple:

  1. import fp-multik
  2. create in first argument of multik - selector from initial arguments
  3. write in the rest of the arguments what you want to process in the predicates
  4. enjoi!

Installation

NPM

npm install fp-multik

Yarn

yarn add fp-multik

Features

  • 🐣 small API and size
  • 🌊 pipable
  • 🙌🏻 usefull access to data/selector in predicates
  • 🔗 better types than in analogues

Usage

multik is a single function:

multik(
  selectorFunction(...initialArgs) => selector,
  [pricidateValue, actionFunction(selector, ...initialArgs) => result],
  [predicateFunction(selector, ...initialArgs), actionFunction(selector, ...initialArg) => result],
  [defaultFunction?(selector, ...initialArgs) => result]
): (initialArgs) => result;
  • pricidateValue can be primitive value or Array / Object
  • predicateFunction is a classic predicate function that return boolean
  • defaultFunction is optional function for specify default result

Simple predicate as value

Matching Number values

import multik from 'fp-multik';

const nominalDegreesOfThousand = multik(
  (n: number) => n,
  [1000, () => 'thousand'],
  [1000000, () => 'million'],
  [1000000000, () => 'billion'],
  [1000000000000, () => 'trillion'],
  [1000000000000000, () => 'quadrillion'],
);

nominalDegreesOfThousand(1000); // 'thousand'
nominalDegreesOfThousand(1000000); // 'million'
nominalDegreesOfThousand(1000000000); // 'billion'
nominalDegreesOfThousand(1000000000000); // 'trillion'
nominalDegreesOfThousand(1000000000000000); // 'quadrillion'

Matching String values

import multik from 'fp-multik';

const greet = multik(
  (data) => data.lang,
  ["english", () => "Hello"),
  ["french", () => "Bonjour")
);

greet({ id: 1, lang: "french" }); // "Bonjour"

Matching Array values

import multik from 'fp-multik';

const shot = multik(
  (data) => data.coord,
  [[30, 40], () => 'hitted!'],
  [[90, 40], () => 'hitted your building!'],
);

shot({ coord: [30, 40] }); // "hitted!"
shot({ coord: [90, 40] }); // "hitted your building!"
shot({ coord: [0, 0] }); // undefined

Matching Object values

import multik from 'fp-multik';

interface Response {
  code: number;
}

const getResult = multik(
  (data: Response) => data,
  [{ code: 200 }, () => 'complete'],
  [{ code: 500 }, () => 'error'],
);

getResult({ code: 200 }); // "complete"
getResult({ code: 500 }); // "error"

Custom predicate

import multik from 'fp-multik';

const fizzBuzz = multik(
  (n: number) => n,
  [(n) => n % 3 === 0 && n % 5 === 0, () => 'FizzBuzz'],
  [(n) => n % 3 === 0, () => 'Fizz'],
  [(n) => n % 5 === 0, () => 'Buzz'],
);

fizzBuzz(3); // "Fizz"
fizzBuzz(5); // "Buzz"
fizzBuzz(15); // "FizzBuzz"

OR predicate

import multik from 'fp-multik';

enum UserRole {
  Admin = 'admin',
  Guest = 'guest',
  Editor = 'editor',
}
type User = { fullname: string; age: number; role: UserRole };

const adminUser: User = { fullname: 'John Smith', age: 20, role: UserRole.Admin };
const guestUser: User = { fullname: 'Evan Martinez', age: 24, role: UserRole.Guest };
const editorUser: User = { fullname: 'Tod Parker', age: 17, role: UserRole.Editor };

const getInformation = multik(
  (data: User) => data.role,
  [[UserRole.Admin, UserRole.Editor], () => 'secret information'],
  [UserRole.Guest, () => 'no access'],
);

getInformation(adminUser); // "secret information"
getInformation(editorUser); // "secret information"
getInformation(guestUser); // "no access"

Default predicate

import multik from 'fp-multik';

const greet = multik(
  (data) => data.lang,
  ["english", () => "Hello"),
  ["french", () => "Bonjour"),
  [() => 'not matched'] // default method
);

greet({ id: 1, lang: "germany" }); // "not matched"

Access to initial arg and selector

import multik from 'fp-multik';

const adultInformation = multik(
  (user) => user.age,
  [(age) => age >= 18, (user, age) => `hey ${user.name}, your age (`${age}`) is right, access success!`),
  [(_user, age) => `your age (${age}) less 18, access denied`]
);

adultInformation({ name: 'Greg', age: 17 }); // "your age (17) less 18, access denied"
adultInformation({ name: 'John', age: 27 }); // "hey John, your age (27) is right, access success!"

Placeholder convection for unused params

If you want use only concrete arguments in predicate or action and ignore other you can name param start underscore:

import multik from 'fp-multik';

const calc = multik(
  (_n1: number, op: string, _n2: number) => op,
  ['+', (_selector, n1, _op, n2) => n1 + n2],
  ['-', (_selector, n1, _op, n2) => n1 - n2],
);

calc(1, '+', 2); // 3
calc(4, '-', 2); // 2

that changes show unused params in callback and exclude some conflicts with names

Use-cases

Control flow

import multik from 'fp-multik';
import process from 'process';

const app = multik(
  (args: string[]) => args[2],
  ['--help', () => console.log('Show help information')],
  ['--run', () => console.log('Run job')],
  [() => console.log('Command not found')],
);

app(process.argv);
user % ts-node app.ts --help
Show help information
user % ts-node app.ts --run
Run job
user % ts-node app.ts
Command not found

Handling business scenarios

import multik from 'fp-multik';

const convertFile = multik(
  (filepath: string, format: string) => format,
  ['json', (format, filepath) => console.log(`Convert ${filepath} as JSON...`)],
  ['html', (format, filepath) => console.log(`Convert ${filepath} as HTML...`)],
  ['csv', (format, filepath) => console.log(`Convert ${filepath} as CSV...`)],
  [(format, filepath) => console.log(`Convert ${filepath} by default as TXT...`)],
);

convertFile('/Users/file1.data', 'json');
convertFile('/Users/file1.data', 'html');
convertFile('/Users/file1.data', 'csv');
convertFile('/Users/file1.data', 'unknown');
user % ts-node app.ts
Convert /Users/file1.data as JSON...
Convert /Users/file1.data as HTML...
Convert /Users/file1.data as CSV...
Convert /Users/file1.data by default as TXT...

Handling error

import multik from 'fp-multik';

const handleFetchError = multik(
  (clientError: HttpClientError) => clientError.code,
  [
    404,
    () => {
      /* ... handle 404 code */
    },
  ],
  [
    500,
    () => {
      /* ... handle 500 code */
    },
  ],
);

try {
  const response = await fetch('http://mysite.ru', {
    method: 'POST',
    body: JSON.stringify(data),
  });
  return await response.json();
} catch (e: HttpClientError) {
  handleFetchError(e);
}

Handling state

import multik from 'fp-multik';

type Action = {
  type: string;
  id?: number;
  text?: string;
};

type Store = {
  add: (text: string) => void;
  remove: (id: number) => void;
  toggle: (id: number) => void;
};

const store: Store = {
  add(text: string) {
    console.log(`todo with ${text} added`);
  },
  remove(id: number) {
    console.log(`${id} todo removed`);
  },
  toggle(id: number) {
    console.log(`#${id} todo toggled`);
  },
};

const handleAction = multik(
  (action: Action, store: Store) => action.type, // custom dispatch
  ['ADD_TODO', (_type_, action, store) => store.add(action.text!)],
  ['REMOVE_TODO', (_type, action, store) => store.remove(action.id!)],
  ['TOGGLE_TODO', (_type, action, store) => store.toggle(action.id!)],
);

handleAction({ type: 'ADD_TODO', text: 'Eat banana' }, store); // log "todo with Eat banana added"
handleAction({ type: 'TOGGLE_TODO', id: 1 }, store); // log "#1 todo toggled"

Alternatives

Let's overview simple code with multik:

import multik from "fp-multik";

const greet = multik(
  (data) => data.lang,
  ["english", () => "Hello"),
  ["french", () => "Bonjour")
);

greet({ id: 1, lang: "french" }); // "Bonjour"

you can also consider alternatives

Lodash (Ramda like libs)

If you love lodash and you dont want install multik - you can implement DIY multik yourself. 😉

import _ from 'lodash';

const multik = (dispatcher, predicates) =>
  _.flow([
    (data) => dispatcher(data),
    _.cond([...predicates, [_.stubTrue, _.constant('no match')]]),
  ]);

const greet = multik(
  (data) => data.lang,
  [
    [(lang) => lang === 'english', () => 'Hello'],
    [(lang) => lang === 'french', () => 'Bonjour'],
  ],
);

greet({ id: 1, lang: 'french' }); // "Bonjour"

@arrows/multimethod

Powerful multimethod library. You can

import { multi, method } from '@arrows/multimethod';

const greet = multi(
  (data) => action.lang,
  method('english', () => 'Hello'),
  method('french', () => 'Bonjour'),
  method(() => 'no match'),
);

greet({ id: 1, lang: 'french' }); // "Bonjour"

Also you can discover next libraries:

Contributing

Your feedback and contributions are welcome. If you have a suggestion, please raise an issue. Prior to that, please search through the issues first in case your suggestion has been made already. If you decide to work on an issue, or feel like taking initiative and contributing anything at all, feel free to create a pull request and I will get back to you shortly.