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

comparator-pipeline

v1.2.3

Published

Configurable comparator for use in the array.sort() call

Downloads

16

Readme

comparator-pipeline

Configurable comparator for use in the array.sort() call

Features

  • Chained call of any length results in a comparator function
  • Short and simple syntax
  • Support for mapping values
  • Extendable by adding more comparators or mappers

Usage examples

import Comparator from 'comparator-pipeline';

array.sort(Comparator);
array.sort(Comparator.descending);
array.sort(Comparator.map(v => something(v)).reversed);
array.sort(Comparator.key('a').natural.key('b').reversed);

Changelog

Install

$ npm install comparator-pipeline --save

API

The Comparator is a function used to compare two values and return < 0, 0 or > 0. Each Comparator call will return the comparator function (e.g. Comparator or Comparator.natural or Comparator.natural.reversed). The Comparator contains a pipeline of steps. The pipeline is performed left to right.

Make sure to call the pipeline steps in the right order because otherwise some may be ignored.

So Comparator.natural.reversed works as expected. But Comparator.reversed.natural will not be reversed because the actual sort is after the reversed step.

When no comparator is added to the pipeline, the default comparator (literal) will be used. (which will be called when needed so, for example, Comparator.reversed.natural will call the default comparator to get the reversed value, then call the natural comparator)

Possible pipeline steps:

| Comparators | | | --- | --- | |literal | Compare using < and >. (default) |natural | Compare '8' before '20' when strings. (for a natural sort) |locale | Compare using localization. Is configurable, so calls with args as in String.localeCompare(...) without the first, or empty for defaults.

|valueMappers | | | --- | --- | |text | Map values to string (alias: string[s]). |number[s] | Map values to floats (using parseFloat). |map(mapper)| Map values using a mapper function. Mapper should be in the form (value, initialValue) => value. value is the value to compare (which can be different from initialValue because it may have been mapped before). initialValueis the initial array element. |ignoreCase | Maps values to lowercase string. |trim | Maps values to trimmed string.

|resultMappers| | | --- | --- | |reverse[d] | Reverse results (ascending <-> descending). Comparators are ascending by default. |ascend[ing] | Results are made ascending (a-z). (actually reverses if reversed) |descend[ing]| Results are made descending (z-a). (actually reverses if not reversed)

|other steps | | | --- | --- | |key(name) | Alias of .orElse.map((val, obj) => obj[name]). |orElse | Stops the pipeline if the previous comparison is non-0 (non-equal). Otherwise resets and continues as if new. |end | Stops the pipeline, even if there is no result. This may be needed when using a configurable function without braces as last step.

|configure | | | --- | --- | |setup(...) | Configure from strings, like 'natural', 'reversed'.

Examples:
array.sort( Comparator )
array.sort( Comparator.reversed )
array.sort( Comparator.natural.descending )
array.sort( Comparator.locale.ascending )
array.sort( Comparator.key('foo').literal.strings )
array.sort( Comparator.map( obj => obj.n * obj.a ).numbers )
array.sort( Comparator.key('foo').map( (k, obj) => k * obj.a ).numbers )
array.sort( Comparator.key('a').reversed.key('b').numbers.key('c').literal.strings )
array.sort( Comparator.map((v,o)=>o.a).ascending.orElse.map((v,o)=>o.b).descending )
array.sort( Comparator.setup('key', 'a', 'numbers', 'reversed') )
array.sort( Comparator.locale('de') )
array.sort( Comparator.locale() )
array.sort( Comparator.locale.end ) // ".end" because of no braces for configurable

Example: Key

Comparing an array of objects:

let array = [ { firstName:..., lastName:..., age:... }, ... ]

array.sort(Comparator.key('lastName').natural
                     .key('firstName')
                     .key('age').descending);

This will sort the array on lastName (natural ascending), except when the lastName is equal which will sort on firstName (literal ascending), except when the firstName is equal which will sort on age (literal descending).

The key(String) call will reset previous settings like reversed or natural. It will stop the pipeline when a non-0 (non-equal) result was achieved.

Alternatively the 'orElse' can be used:

array.sort(Comparator       .map((o,v) => o.lastName).natural
                     .orElse.map((o,v) => o.firstName)
                     .orElse.map((o.v) => o.age).reversed );

Extending pipeline functionality

Functionality can be added by assigning functions to Comparator:

Comparator[.type].name = function(...)

All steps can be added via the 'steps' type. A step function is called with a pipeline state. The this of the function is also pipeline state.

A few convenience types are available that don't have to use the pipeline state directly:

| Type | Parameters | Return | Description | | --- | --- | --- | --- | |comparators | valueA, valueB | < 0, 0 or > 0 | comparator functions (default when type is omitted). must be ascending. |valueMappers | value, initialValue | new value | Value mapper functions. |resultMappers| result | new result | Result mapper functions.

  • The 'comparators' type is default and can be omitted.
  • When using the extension, the type (valueMappers, resultMappers, etc) should be omitted.
  • All names should be unique.
  • When an extention requires configuration, use 'configurable' (see below)

The Comparator-pipeline is called for each comparison needed to sort the array. So it will be called with two array items and should return < 0, 0 or > 0. Each pipeline-step is called until the end or until a pipeline step determines the pipeline is finished.

'steps' functions get a pipeline-state to update.

The pipeline-state has the following fields:

  • a value to compare
  • b value to compare against
  • initialA initial value of a (a may have changed due to mapping)
  • initialB initial value of b (b may have changed due to mapping)
  • result the comparison result, or undefined
  • isFinished true when no more steps in the pipeline should be executed
  • pipeline array of steps in the pipeline.
  • step current step. Step looks like { type, name, func }
  • comparator last used comparator (e.g. the literal or natural function)
  • checkResult() calls getResult() if result is undefined
  • getResult() uses comparator (default comparator if not set) to set result.
  • hasResult() true when result is not undefined and not 0.
  • reset() sets state back to begin: no result, not finished, initial a/b, comparator default.

A step function can update the current state, for example by changing a/b or setting the result.

A step function can be made configurable by setting 'configurable' in the path. The step function should return a function then:

Comparator.steps.configurable.test = (cfg1, cfg2) => state => do something with cfg1, cfg2 and state.

Examples:
Comparator.valueMappers.abs = val => Math.abs(val);
array.sort( Comparator.numbers.abs )

Comparator.valueMappers.ignoreCase = v => String(v).toLowerCase();
array.sort(Comparator.ignoreCase)

Comparator.valueMappers.onFirstCapital = v => v.replace( /^(.*?)([A-Z][^ ]+)/, '$2$1' );
array.sort(Comparator.onFirstCapital)

Comparator.resultMappers.reversed = result => -result;
array.sort( Comparator.numbers.abs.reversed );

Comparator.steps.stop = state => state.isFinished = true;

Extension configuration

When an extension requires configuration input, the 'configurable' key should be used, and the function should return a function.

Comparator.configurable.example = (...cfg) => (a, b) ==> { something with a, b, cfg };

For example in Comparator.abs.reversed, 'abs' does not have any configuration. But in Comparator.key('a').reversed, 'key' has one or more key-names as configuration.

(the explicit use ('configurable') is better than guessing if the function returns a function)

Overwriting functionality

It is also possible to overwrite existing handlers. For example the default 'numbers' valueMapper is this:

Comparator.valueMappers.numbers = v => parseFloat(v);

which you can change (for example) to:

Comparator.valueMappers.numbers = v => parseInt(v);

or let trim also trim braces:

Comparator.valueMappers.trim = v => v.replace( /^[{(\s]+|[})\s]+$/g, '' );

Existing functionality can be removed by using the delete keyword, without the type.

delete Comparator.trim

License

MIT (C) Nicolas de Jong