Utility methods for creating comparators, ie. function (left,right)=>number
. Slightly more versatile than other packages I found.
Documentation for all methods with examples.
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
.
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]
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 laura
s 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.
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]
Probably not going to work on Windows.
git clone https://github.com/blutorange/js-kagura
cd js-kagura
npm install
npm run build
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.undefined
. Some methods can handle undefined by setting the type parameter to T|undefined
on the calling side.undefined
values. See above
for details.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";
Senran Kagura. Which part of the human body are they always concerned with and keep comparing?
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 of the objects to compare.
Type of the key produces by the given key extractor.
Takes one argument, the object, and returns the key to compare by.
Compares the two keys extracted by keyExtractor. Default to the natural order, eg. by using < and >.
The comparator comparing by key.
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 of the objects to compare.
How to access access the property to compare by, eg "name", "parent.id".
How to compare the fields. Defaults to natural order.
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.
Threshold for checking equality between two numbers. Defaults to 1E-12.
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 of the objects to compare.
List of comparators to compare by.
The combined comparator.
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 of the objects to compare.
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 of the objects to compare.
A predicate that returns true iff it is passed a value equal to the given item.
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 of the objects to compare.
Comparator for checking equality between two items.
An equator returning true for two items iff the given comparator considers them equal.
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 of the objects to compare.
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 of the objects to compare.
Comparator to invert.
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 of the objects to compare.
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 of the items to be sorted.
Array to sort.
Comparator to use for the comparison.
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.
Type of the items to be sorted.
Array to sort.
Extracts the key to be used for comparing the items.
Comparator to use for comparing the keys.
Similar to sort, but additionally ensures that the sort is stable, ie. that items that are equal retain their relative position in the array.
Type of the items to be sorted.
Array to sort.
Comparator to use for the comparison.
Similar to sortBy, but allows specifying a custom key extractor.
Type of the items to be sorted.
Array to sort.
Extracts the key to be used for comparing the items.
Comparator to use for comparing the keys.
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:
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 of the objects to compare.
Lower bound.
Upper bound.
Generated using TypeDoc
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"]