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

foci

v0.0.2

Published

Lens-like functionality with a lodash-style interface.

Downloads

13

Readme

foci

Foci is a lodash inspired lens library.

A lens is a path into an object, which can be used to extract its values, or even "modify" them in place (by creating a new object with the value changed).

When writing immutable code, we very commonly end up with deeply nested data stores, e.g.:


const store = {
	users: [
		{name: 'Jack Sparrow', 
		 posts: [
		 	{title: 'Why is the rum always gone? A dissertation on Carribean trade deficit'}
		 	],
		 	...
		 },
	...
	]
}

And updating a deeply nested structure will require heavy usage of the spread operator (or Object.assign). E.g. To capitalize the title of the first post of the first user, you would write:

const userIdx = 0;
const postIdx = 0;
const capitalize = (string) => {...}

{...store,
	users: store.users.map((user, idx) => (
		idx === userIdx 
		? {...user, 
			  posts: user.posts.map((post, idx) =>
					idx === postIdx
					? {...post,
	 						title: capitalize(post.title)
					  }
					: user)
		  }
		 : user
		)
}

This is an enormous amount of obfuscating boiler plate code for a very simple update.

With lenses, we could write this update much more declaratively:

mod(`users[${userIdx}].posts[${postIdx}]`)
	(capitalize)
	(store)
	

API

lens

A lens is simply a string which describes a path into an object. It can include object accesses and array indicies.

The focus of the lens is the final value referenced in the path.

Lenses can be constructed with the lens function, or passed as string literals into the lens consumer functions (get, set, mod)

Combining lenses with ES6 template strings can be a concise way to use environment variables to create a dynamic path.

> "a.b[3].d" // focus is the d field

> const idx = 10
> `a.b[${idx}]` // focus is the 11th element of b

get :: (string or lens) -> obj -> focus

get consumes a lens and produces a function that takes in an object obj and outputs the focus of its lens.

> get('a.b.c')({a: {b: {c: 7}}})
7

set :: (string or lens) -> const -> obj -> obj

set consumes a lens and produces a function that takes in a constant value const, and produces a function consuming an object obj and outputs a clone of obj with the focus of the lens replaced with const

> set('a.b.c')(10)({a: {b: {c: 7}}})
{a: {b: {c: 10}}}

mod :: (string or lens) -> (a -> a) -> obj -> obj

mod consumes a lens and produces a function that takes in a modifiying function m for the focus of the lens, and produces a function consuming an object obj, then outputs a clone of obj with the focus of the lens replaced with m's output.

> const inc = n => n + 1
> mod('a.b.c')(inc)({a: {b: {c: 7}}})
{a: {b: {c: 8}}}

lens :: string -> lens

lens consumes a path into an object and produces a corresponding lens function. All lens consumers (get, set, mod) will accept a literal string. However, lenses are composable via compose, so lenses can be built piece by piece.

compose :: (lens or string)* -> lens

compose composes the foci of multiple lenses

> const l = compose('a.b', 'c.d')
> get(l)({a: {b: {c: {d: 10}}}})
10