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

trine

v0.1.1

Published

A utility library for modern JavaScript.

Downloads

5

Readme

Trine

Join the chat at https://gitter.im/jussi-kalliokoski/trine

Build Status Coverage Status

A utility library for modern JavaScript.

Trine is a utility library geared at tapping the full potential of functional programming in JS, in the vein of lodash, underscore and Ramda.

See the latest docs/documentation for a full API reference.

Why?

At this point, you should be asking yourself: "why yet another utility library", and you'd be right in asking so. Ramda argues that lodash and underscore put the data in the wrong place: at the first parameter of the function, while it should be at the last position. They're both (subjectively) wrong: the natural place for data in JS is the this parameter.

Functional programming in JS is awkward. ES5 introduced some improvements, and ES6 introduces even more. However, even if you're writing ES6 today, you are probably still seeing code like this when attempting to write in a functional style:

flatten(
    items
        .filter(isOk)
        .map(toOtherType)
)

And this is just a simple example. The data as an argument style just doesn't fit in well with the builtins of JS, and makes it hard to reason about the order of transformations. What you'd really want to do is this:

items
    .filter(isOk)
    .map(toOtherType)
    .flatten()

but extending the builtin prototypes is a bad practice, even in non-library code, and has caused numerous issues with standardizing new features to builtins in JS. The function bind syntax proposal fixes this issue. The previous example could be written as follows:

items
    .filter(isOk)
    .map(toOtherType)
    ::flatten()

Much better. But that's not all. Let's imagine we had a custom map and reduce methods that passed each item as this to the transformer function, as well as some other helpers. This would allow us to compose using the builtins, as so:

const characters = strings
    ::map(String.prototype.split::partial(""))
    ::reduce(Array.prototype.concat)
    .sort()
    ::uniq();

But why stop there? ES6 introduces the concept of iterators to JS. Iterators are a protocol that most collection types in ES6 (Map, Set, Array, etc.) implement, by exposing a function under the Symbol.iterator symbol. This means you can also extend your custom collection types to support the same protocol, and generator functions support it too. Iterators are a very flexible abstraction over collections, and unlike memory-bound collections, can also represent infinite sets, such as the Fibonacci series or prime numbers. In a memory-bound collection an infinite number of items would require infinite memory.

Iterators, on the other hand, allow us to process only as much as we need. So let's say we wanted to find the first 5 common items (intersection) of two infinite sets, the Fibonacci series and the prime numbers. The following is a working example of Trine, as Trine's all collection methods are actually methods for iterables:

function * fibonacci () {
    let i0 = 1;
    let i1 = 0;

    while ( true ) {
        yield i0;
        [i0, i1] = [i0 + i1, i0];
    }
}

function * primes () {
    const primes = new Set();
    yield 1;
    yield 2;

    loop: for ( let i = 3; true; i += 2 ) {
        for ( const prime of primes ) {
            if ( i % prime === 0 ) { continue loop; }
        }

        primes.add(i)
        yield i;
    }
}

const commonItems = [fibonacci(), primes()]
    ::intersection(sub)
    ::head(5)
    ::to(Array);
console.log(commonItems); // logs [1, 2, 3, 5, 13]

Using iterators also allows us to easily timebox even synchronous operations. Let's say we wanted to get the 1000 first common items, but we'd be ok with less if it would block for too long:

const deadline = Date.now() + 1000;
const commonItems = [
    fibonacci()::takeWhile(() => Date.now() < deadline),
    primes(),
]
    ::intersection(sub)
    ::head(1000)
    ::to(Array);

if ( commonItems.length < 1000 ) {
    console.error("Sorry, asked for 1000 items but got only %s :(", commonItems.length);
}

console.log(commonItems);

There are some other practical applications to this as well. Say we have a set of products, and we want to show a page that lists the first 15 items sorted ascending by price. If our product catalogue is prohibitively long, sorting all the items ascending by price would take too long. With iterators, however, we can perform this orders of magnitude faster than with in-memory collections. Trine ships with an implementation of the quicksort algorithm that works with iterators. Using this, we only do precise sorting for the 15 first items, as so:

function byPrice (b) {
    return this.price - b.price;
}

const firstPageItems = products
    ::quickSort(byPrice)
    ::head(15)
    ::to(Array);

That's it! Most of the time you won't even have to be thinking about performance, because with iterators you can just compose the manipulations to produce the result you need and good performance will be the default. I welcome all micro-benchmarks, but doing less work will always be faster than doing more work.

Maybe

Iterators have one more trick up their sleeve. They provide a handy way to wrap missing data, instead of implementing error handling at every turn. For example:

function getItemPriceById (id) {
    const result = this
        ::find(function () {
            return this.id === id;
        })
        ::map(prop("price"))
        .next();

    if ( result.done ) { throw new Error("Item not found"); }
    return result.value;
}

Modules

Trine has been designed to be modular and decoupled from the ground up. Each exposed function is in its own module, so for example if you're using Webpack, instead of a huge library, you will only be transferring the needed functions to the client.

Installation

Trine is available on npm:

npm install --save trine

License

Trine is ISC licensed. See the LICENSE document for more information.

Contributing

The issues are a good place to start. Whether you're having a bug or fix one, have a knack for design and want to improve the appeal of Trine, want to improve the documentation, add a test case, propose a feature or just ask for help, please do. All (friendly) contributions are welcome, as long as they're not malicious.