Options
All
  • Public
  • Public/Protected
  • All
Menu

Utility methods for creating comparators, ie. function (left,right)=>number. Slightly more versatile than other packages I found.

  • ~ 700 bytes minified + gzipped without browser polyfills etc.

Documenation

Documentation for all methods with examples.

Install

The drill:

npm install --save kagura

Typings for typescript are available.

Use the dist.js or dist.min.js for browser usage. Exposes a global object window.Kagura.

Usage

Compare objects by their id property:

// import this lib
const { byKey } = require("kagura")

// create array and sort it with a custom comparator
const array = [ {id: 6}, {id: 3} ]

array.sort(byKey(item => item.id));

If you are comparing simply by some property, you can also use byProp:

const { byProp } = require("kagura")
array.sort(byProp("id"))

Compare objects by their data.id property in descending order:

byProp("data.id", inverse) // preferred

// equivalently you couse use
invert(byProp("data.id"))
byKey(item => - item.data.id)
byKey(item => item.data.id, inverse)

Compare objects by their lastName property first, then firstName, then age.

combine(
  byProp("lastName"),
  byProp("firstName"),
  byProp("age")
)

Find all items equal to "cake".

["cake", "chocolate"].filter(equalTo("cake"))

["cake", "Cake", "chocolate"].filter(equalTo("cake"), ignoreCase)

[{name: "cake"}, {name: "chocolate"}]
  .filter(equalTo("cake", byKey("name")))

[{name: "cake"}, {name: "Cake"}, {name: "chocolate"}]
  .filter(equalTo("cake", byKey("name", ignoreCase)))

Compare objects by using the comparison operator > and <.

[9,7,8].sort(natural) // => [7,8,9]
[9,7,8].sort(inverse) // => [9,8,7]

Handling undefined

Sort order of undefined

undefined always compares as less than any other value. This is different than how Array#sort handles undefined, but in line with the idea that an undefined string represents a blank string, which sorts before other strings alphabetically. This behaviour is also useful when working with comparisons on multiple properties.

To illustrate this, consider the following list of users, sorted first by their given name, then by their family name.

const user1 = {given: "dave", family: "oxford"};
const user2 = {given: "dave", family: "carbide"};
const user3 = {given: "laura", family: "oxford"};
const user4 = {given: "laura", family: "borea"};
const user5 = {given: "odo", family: "frodo"};
const users = [user1, user2, user3, user4, user5];
const comparator = combine(byProp("given"), byProp("family"));
users.sort(comparator);
// => [user2, user1, user4, user3, user5]

Now assume we want to get all users whose given name is laura. We could iterate over all entries and apply a filter:

users.filter(user => user.given === "laura");

This works, but this takes O(n) time to run. Assuming the list is already sorted, a binary search runs in O(log(n)) time. We construct a virtual user object with only a given name {given: "laura"} and determine the position where it would sort in the ordered list.

const search = {given: "laura"};
const start = users.findIndex(user => comparator(search, user) <= 0);
// start === 2  

Since undefined sorts before any other value, the start position now points to the first user in the sorted list with a given name of laura. Now we can find all lauras by iterating from the start position until we encounter a user with a different given name.

for (let user = users[start]; user.given === `laura`; user= users[++start]) {
  // do something with the user
  console.log(user);
}
// => logs user4 and user3

This approach also works well with binary search trees that always keep their elements sorted.

Sorting arrays

The built-in array sort method always sorts undefined last, irrespective of the given comparator. Use one of the wrapper methods to sort undefined properly:

const { sort } = require("kagura");

// Comparator "natural" sorts undefind before any other value.
// But the built-in function always puts undefined last.
[1,2,undefined,3].sort(natural);
// => [1,2,3,undefined]

sort([1,2,undefined,3], natural)
// => [undefined, 1, 2, 3]

Build

Probably not going to work on Windows.

git clone https://github.com/blutorange/js-kagura
cd js-kagura
npm install
npm run build

Change log

I use the following keywords:

  • Added A new feature that is backwards-compatible.
  • Changed A change that is not backwards-compatible.
  • Fixed A bug or error that was fixed.

1.2.0

  • Added sort wrappers, see above.
  • Fixed some typings regarding undefined. Some methods can handle undefined by setting the type parameter to T|undefined on the calling side.

1.1.0

  • Updated all methods to handle undefined values. See above for details.
  • Extracted common interfaces and types (Comparator, Predicate etc.) to their own package. This affects you only if you are using typescript and are referring to these types explicitly; in this case please import them from the package andross instead from this package kagura, see below.
// If using typescript, change this
import { Comparable, Comparator, Equator, KeyExtractor, Predicate } from "kagura";
// to
import { Comparable, Comparator, Equator, KeyExtractor, Predicate } from "andross";

Teh name

Senran Kagura. Which part of the human body are they always concerned with and keep comparing?

Index

Variables

Const ignoreCase

ignoreCase: Comparator<Maybe<string>> = byKey<string, string>(item => item.toLowerCase())

A comparator for comparing strings without regards to case.

// capitals come before lower-case letters
["Dave", "eve", "Eve", "dave"].sort()
// => ["Dave", "Eve", "dave", "eve"]

// case-insensitive sort
// equal names have no order
["Dave", "eve", "Eve", "dave"].sort(ignoreCase)
// => ["Dave", "dave", "eve", "Eve"]

// case-insensitive sort
// if two names are equal, list lower-case first
["Dave", "eve", "Eve", "dave"].sort(combine(ignoreCase, inverse))
// => ["dave", "Dave", "eve", "Eve"]

Functions

byKey

  • byKey<T, K>(keyExtractor: KeyExtractor<T, K>, keyComparator?: Comparator<K>): Comparator<Maybe<T>>
  • Compares two objects by computing a key for each object, and the comparing these keys against each other. undefined gets sorted before any other value.

    const games = [
      { title: "Bus adventure", reviews: { ign: 8, USGamer: 7} },
      { title: "jump kicking", reviews: { ign: 4, USGamer: 6} },
      { title: "The abyss 6", reviews: { ign: 7}, USGamer: 9} }
    ]
    
    const byRating = byKey(game => game.reviews.ign + game.reviews.USGamer);
    
    // sort games by their combined rating
    games.sort(byRating)
    // => "jump kicking", "Bus adventure", "The abyss 6"
    
    // sort games by their combined rating, inverse order
    games.sort(inverse(byRating))
    // => "The abyss 6", "Bus adventure", "jump kicking"
    
    // sort games by their name, ignoring case
    games.sort(byRating, byKey(game => game.title, ignoreCase))
    // => "Bus adventure", "jump kicking", "The abyss 6"
    

    Use it to group notebook by their sceen type:

    const notebooks = [
      {name: "Mason 42-1", screen: "LED"}
      {name: "Lolipop 4", screen: "PLASMA"},
      {name: "Zimbabwe AXR", screen: "OLED"},
      {name: "Powerlancer 4K", screen: "OLED"},
      {name: "Clay HiPx 9", screen: "PLASMA"},
      {name: "Officestation KX-72-3", screen: "LED"}
    ]
    
    // index notebooks by screen
    import { Methods } from "elbe";
    Methods.group(notebooks, byKey("screen"))
    // => {LED: [...], PLASMA: [...], OLED: [...]}
    

    Type parameters

    • T

      Type of the objects to compare.

    • K

      Type of the key produces by the given key extractor.

    Parameters

    • keyExtractor: KeyExtractor<T, K>

      Takes one argument, the object, and returns the key to compare by.

    • Default value keyComparator: Comparator<K> = natural

      Compares the two keys extracted by keyExtractor. Default to the natural order, eg. by using < and >.

    Returns Comparator<Maybe<T>>

    The comparator comparing by key.

byProp

  • byProp<T>(keySpecifier: string, comparator?: Comparator<any>): Comparator<Maybe<T>>
  • Compares objects by accessing a property of the objects. For example, to compare objects by their name property, use "name" as the keySpecifier. Use a dot to compare by a nested property, eg. to compare objects by the id property of the object's parent property, use "parent.name".

    // sorts books by their isbn property
    const books = [ { isbn: "2454396543965" }, { isbn: "2147865437664" } ]
    books.sort(byProp("isbn"))
    // => [ { isbn: "2147865437664" }, { isbn: "2454396543965" } ]
    
    // sort kids by their parent -> name property
    const kids = [ { parent: { name: "Sam"} }, { parent: {name: "Carly"} } ];
    kids.sort(byProp("parent.name"))
    // => [ { parent: { name: "Carly"} }, { parent: {name: "Sam"} } ]
    
    // sort employees by their 'department.manager' property
    const employees = [ { "department.manager": "Joe" }, { "department.manager": "Amy"} ]
    employees.sort(byProp("department\\.manager"))
    // => [ { "department.manager": "Amy" }, { "department.manager": "Joe"} ]
    
    // sort chairs by their material, case-insensitive
    const chairs = [ {material: "Wood"}, {material: "plastic"} ]
    chairs.sort(byProp("material", ignoreCase))
    // => [ {material: "plastic"}, {material: "Wood"} ]
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • keySpecifier: string

      How to access access the property to compare by, eg "name", "parent.id".

    • Optional comparator: Comparator<any>

      How to compare the fields. Defaults to natural order.

    Returns Comparator<Maybe<T>>

byThreshold

  • byThreshold(threshold?: number): Comparator<Maybe<number>>
  • Compares two numbers with a treshold. If the difference between two numbers is smaller than the treshold, they compare as equal. undefined is sorted before any other value. NaN is sorted after any other value.

    [2, 3, 0, Infinity, undefined, -Infinity, NaN, NaN, -9].sort(byThreshold(0.5))
    // => [ undefined, -Infinity, -9, 0, 2, 3, Infinity, NaN, NaN ]
    
    const animals = [
      { name: "alligator", lifeExpectancy: 68},
      { name: "turtle", lifeExpectancy: 123}
      { name: "ant", lifeExpectancy: 0.5}
      { name: "elephant", lifeExpectancy: 70}
      { name: "beaver", lifeExpectancy: 20}
      { name: "bee", lifeExpectancy: 1}
      { name: "swan", lifeExpectancy: 10}
      { name: "leopard", lifeExpectancy: 17}
      { name: "mosquitofish", lifeExpectancy: 2}
    ]
    
    data.filter(equalTo(byTreshold(0.5), 1))
    // => "ant", "bee"
    
    data.filter(equalTo(byTreshold(10), 70))
    // => "alligator", "elephant"
    

    This comparator is not transitive and should not be used for sorting items:

    const comparator = byThreshold(0.5);
    
    comparator(0, 0.3) // => 0
    comparator(0.3, 0.6) // => 0
    comparator(0, 0.6) // => -1
    
    [0.6, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7].sort(comparator)
    // => [ 0, 0.1, 0.6, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 ]
    // The exact order may be dependent on the implementation.
    

    Parameters

    • Default value threshold: number = 1e-12

      Threshold for checking equality between two numbers. Defaults to 1E-12.

    Returns Comparator<Maybe<number>>

combine

  • combine<T>(...comparators: Comparator<T>[]): Comparator<T>
  • Compare by multiple criteria. If the first comparator deems two objects equals, the second iterator is used. If it deems the two objects equal as well, the third iterator is used etc. The two objects are equal iff all comparators deem them equal.

    const data = [ {country: "Japan", city: "Tokyo"}, {country: "Japan", city: "Kyoto"} ]
    
    data.sort(combine(byProp("country"), byProp("city")))
    // => [ {country: "Japan", city: "Kyoto"}, {country: "Japan", city: "Tokyo"} ]
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • Rest ...comparators: Comparator<T>[]

      List of comparators to compare by.

    Returns Comparator<T>

    The combined comparator.

comparable

  • comparable<T>(lhs: Maybe<T>, rhs: Maybe<T>): number
  • Comparator for comparable objects, ie. by using their 'compareTo' method. undefined gets sorted before any other value.

    class Vector implements Comparable<Vector> {
       // ...
      compareTo(vector: Vector) {
        return this.abs2() - vector.abs2();
      }
    }
    
    // sort vectors by their length
    [new Vector(5,12), new Vector(3,4)].sort(comparable)
    // => [new Vector(3,4), new Vector(5,12)]
    

    Type parameters

    • T: Comparable<T>

      Type of the objects to compare.

    Parameters

    • lhs: Maybe<T>
    • rhs: Maybe<T>

    Returns number

equalTo

  • equalTo<T>(item: T, test?: Comparator<T>): Predicate<T>
  • Transforms a comparator or Equator into a predicate for testing whether an items equals the given argument.

    const notebooks = [
      {name: "Mason 42-1", screen: "LED"}
      {name: "Lolipop 4", screen: "PLASMA"},
      {name: "Zimbabwe AXR", screen: "OLED"},
      {name: "Powerlancer 4K", screen: "OLED"},
      {name: "Clay HiPx 9", screen: "PLASMA"},
      {name: "Officestation KX-72-3", screen: "LED"}
    ]
    
    // get notebooks with OLED screen
    notebooks.filter(equalTo("OLED", byKey("screen")))
    // => "Zimbabwe AXR", "Powerlancer 4K"
    
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • item: T
    • Default value test: Comparator<T> = natural

    Returns Predicate<T>

    A predicate that returns true iff it is passed a value equal to the given item.

equals

  • equals<T>(comparator?: Comparator<T>): Equator<T>
  • Transforms a comparator into an Equator, ie. into a function return true if its two arguments are equal.

    const cars = [
      {maker: "Opal"}
      {maker: "Marzipan"},
      {maker: "Opal"},
    ]
    
    const sameMaker = equals(byKey("maker"))
    sameScreen(cars[0], cars[1]) // = false
    sameScreen(cars[0], cars[2]) // = true
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • Default value comparator: Comparator<T> = natural

      Comparator for checking equality between two items.

    Returns Equator<T>

    An equator returning true for two items iff the given comparator considers them equal.

inverse

  • inverse<T>(lhs: Maybe<T>, rhs: Maybe<T>): number
  • Natural comparator in inverse order, ie. by using the < and > operators. undefined gets sorted before any other value.

    [0,1,2,3,4,5,6,7,8,9].sort(inverse)
    // => [9,8,7,6,5,4,3,2,1,0]
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • lhs: Maybe<T>
    • rhs: Maybe<T>

    Returns number

invert

  • invert<T>(comparator: Comparator<T>): Comparator<T>
  • Creates a new comparator that is the inverse of the given compartor. undefined gets sorted before any other value.

    // Sort objects by their foo property in reverse order.
    
    const people = [ {name: "Bob"}, {name: "Zoe"}]
    
    people.sort(invert(byProp("name"))
    
    // => [ {name: "Zoe"}, {name: "Bob"}]
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • comparator: Comparator<T>

      Comparator to invert.

    Returns Comparator<T>

    • Inverted comparator.

natural

  • natural<T>(lhs: Maybe<T>, rhs: Maybe<T>): number
  • Natural comparator, ie. by using the < and > operators. undefined gets sorted before any other value.

    [9,8,7,6,5,4,3,2,1,0].sort(natural)
    // => [0,1,2,3,4,5,6,7,8,9]
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • lhs: Maybe<T>
    • rhs: Maybe<T>

    Returns number

sort

  • sort<T>(items: T[], comparator?: Comparator<T>): void
  • A wrapper for the built-in sort function that respects the comparator even if any item is undefined.

    // Comparator "natural" sorts undefind before any other value.
    // But the built-in function always puts undefined last.
    [1,2,undefined,3].sort(natural);
    // => [1,2,3,undefined]
    
    sort([1,2,undefined,3], natural)
    // => [undefined, 1, 2, 3]
    

    Type parameters

    • T

      Type of the items to be sorted.

    Parameters

    • items: T[]

      Array to sort.

    • Default value comparator: Comparator<T> = natural

      Comparator to use for the comparison.

    Returns void

sortBy

  • sortBy<T, K>(items: T[], keyExtractor: KeyExtractor<T, K>, comparator?: Comparator<K>): void
  • Similar to sort, but allows specifying a custom key extractor. This also caches the key and should be used when extracting the key is computationally expensive.

    see

    sort

    Type parameters

    • T

      Type of the items to be sorted.

    • K

    Parameters

    • items: T[]

      Array to sort.

    • keyExtractor: KeyExtractor<T, K>

      Extracts the key to be used for comparing the items.

    • Default value comparator: Comparator<K> = natural

      Comparator to use for comparing the keys.

    Returns void

sortStable

  • sortStable<T>(items: T[], comparator?: Comparator<T>): void
  • Similar to sort, but additionally ensures that the sort is stable, ie. that items that are equal retain their relative position in the array.

    see

    sort

    Type parameters

    • T

      Type of the items to be sorted.

    Parameters

    • items: T[]

      Array to sort.

    • Default value comparator: Comparator<T> = natural

      Comparator to use for the comparison.

    Returns void

sortStableBy

  • sortStableBy<T, K>(items: T[], keyExtractor: KeyExtractor<T, K>, comparator?: Comparator<K>): void
  • Similar to sortBy, but allows specifying a custom key extractor.

    see

    sort

    Type parameters

    • T

      Type of the items to be sorted.

    • K

    Parameters

    • items: T[]

      Array to sort.

    • keyExtractor: KeyExtractor<T, K>

      Extracts the key to be used for comparing the items.

    • Default value comparator: Comparator<K> = natural

      Comparator to use for comparing the keys.

    Returns void

within

  • within<T>(lower: T, upper: T, __namedParameters?: object): Predicate<T>
  • Creates a predicate that checks whether it is passed a value between the given lower and upper bounds. Whether the predicate returns true for the lower and upper bounds is controlled by the given mode:

    • [] Includes the lower and upper bound.
    • () Excluded the lower and upper bound.
    • [) Includes the lower bound and exclude the upper bound.
    • (] Excludes the lower bound and includes the upper bound.
    const foods = [
        {name: "fish & chips", bestBefore: new Date(2002, 05, 09)}
        {name: "chocolate", bestBefore: new Date(2006, 01, 04)}
        {name: "strawberry cake", bestBefore: new Date(2002, 05, 08)}
        {name: "bottled tomatoes", bestBefore: new Date(2005, 11, 26)}
        {name: "onion bread", bestBefore: new Date(2002, 05, 10)}
    ]
    
    // foods I should not eat
    foods.filter(within(new Date(2010), new Date(2015,05,08), byKey("bestBefore"))
    // => "strawberry cake"
    
    // foods I should eat soon
    foods.filter(within(new Date(2015, 05, 09), new Date(2015,05,10), byKey("bestBefore"))
    // "fish & chips", "onion bread"
    
    // all foods I could eat
    foods.filter(within(new Date(2015, 05, 09), new Date(2015,05,10), byKey("bestBefore"))
    // "fish & chips", "chocolate", "bottled tomatoes", "onion bread"
    

    Type parameters

    • T

      Type of the objects to compare.

    Parameters

    • lower: T

      Lower bound.

    • upper: T

      Upper bound.

    • Default value __namedParameters: object = {}

    Returns Predicate<T>

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc