By: Gorm Casper
18 Aug, 2013

Cherries ↠ on github

I often find myself needing a function like map or filter, but unable/unwilling to import an entire library like the famous Lodash or Underscore (whichever you fancy). There can be many reasons for this, but my primary reason is usually that I am building a small plugin to handle some operation for someone, and it is entirely unreasonable of me to expect a client to important an entire library, five times the size of my little plugin, just so I can filter an array properly (or whatever else I need that library for).

So I often have to cherry pick functions from those libraries, but they are not built in a modular way internally. Functions inside the library are highly dependent on other functions in order to save bytes and speed. For instance you can't copy map over without also copying each. So instead, here are some functions that I wrote, that can all (save a few very obvious ones) be exported "as is" into other code. Change them, rename them, make them faster, do whatever you want with them. They are just a starting point, and a small building block for bigger things.

For this reason, I haven't added this "project" (seriously, it's just a file of code) to Bower or any some such. It's not meant to be included as a full library (if that's what you need, then use another library), but just some code that you can search through and copy/paste the stuff you need, and pretend you wrote it yourself.

Important: Since this is not a library meant to be included in any project as is, I will (most likely) at any point in time move things around, change implementations and/or functionality, and even names of functions.

Updates

Examples

We all love examples. Here are some of how this is meant to be used.

I consider compose and autoCurry (which is dependent on curry) to be cornerstones in coding, and would include them in any project. Example 2 and 3 below reflect usage of them. As examples often are, these are a bit contrived.

Example 1

Writing a small plugin, you need a function that flattens a list of lists. You know how to do this... sorta... but it'd be nice if someone else had already done it. So you find it in here and copy/paste it into your own project. No sweat.

var flatten = function(xs) {
  /* find the code in cherries.js ... */
};

And now you're flattening arrays all day long!

Example 2

Let's find the average age of a list of people that looks like this:

var people = [
  {name: 'Bob Roberts', age: 31, gender: 'm'},
  {name: 'Julia Smith', age: 63, gender: 'f'},
  {name: 'Ken Paulsen', age: 23, gender: 'm'},
  {name: 'Lisa Erikson', age: 9, gender: 'f'}
];

For this, we could use pluck and mean.

/* I assume compose and autoCurry is already available */
var pluck = autoCurry( /* code defining pluck */ ),
    mean  = /* code defining mean  */;

And we compose the function we need,

Notice that since pluck is autoCurried, calling it with just one argument (it needs two) will return a function waiting for a list of objects (how convenient) that returns a list of whatever we plucked out (in this case the age).

// Compose a function that first plucks out the age into a list, and then
// takes the mean of that list
var getMeanOfAges = compose(  mean,
                              pluck('age')  );

// Try out our function
getMeanOfAges(people)   // 31.5

Of course, if you only need pluck once, and to do only the above, then you'd probably not include autoCurry and pluck, but rather just define your own function and use that instead. Something like this:

// Quick adaptation of the `pluck` within cherries
var pluckAge = function(xs){
  var result = [], i = -1, len = xs.length;
  while (++i < len) {
    result.push(xs[i].age)
  }
  return result;
};

var getMeanOfAges = compose(mean, pluckAge);

Example 3

You're writing a little plugin for a client and you need an easy way to pull some data representing people out that comes in a list form, you want an object, and you want to pick out certain data.

Here's a sample list we could get from a server or whatever.

var people = [
  ['Bob Roberts', 31, 'm'],
  ['Julia Smith', 63, 'f'],
  ['Ken Paulsen', 23, 'm'],
  ['Lisa Erikson', 9, 'f']
];

First you pick out the functions you need from this library and define them using autoCurry

/* compose and autoCurry assumed */
var pick        = autoCurry( /* code defining pick       */ ),
    map         = autoCurry( /* code defining map        */ ),
    listsToObj  = autoCurry( /* code defining listsToObj */ );

Then we build a function that turns people into an object and gives us a list of of whatever we need

Like in the previous example, the functions are autoCurried, so calling them with less arguments than they expect (map, pick, and listsToObj each need two arguments, we're calling them with one) means we get a function back that is waiting for the last argument.

var getNameAndAgeOf = compose(  map( pick(['name', 'age']) ),
                                map( listsToObj(['name', 'age', 'gender']) ) );

Calling this function on people...

getNameAndAgeOf(people)   // [{"name":"Bob Roberts","age":31}, ... ]

We can make it a bit more general use actually...

var getThisOfThat = function(xs) {
  return compose( map( pick(xs) ),
                  map( listsToObj(['name', 'age', 'gender']) ) );
};

// Make a function that pulls out name and gender
var getNameAndGender = getThisOfThat(['name', 'gender'])

// Try out our new function
getNameAndGender(people)     // [{"name":"Bob Roberts","gender":"m"}, ... ]

See the examples at lt for some more examples of usage.

Functions

Here's a list of each of the functions currently defined. Remember, dig into the code and pull out the bits you need. Don't just import everything; there are better libraries for that.

Jump to: id, isType, replicate, dot, pluck, each, map, filter, reject, partition, find, head, tail, last, initial, take, drop, takeWhile, dropWhile, span, breakList, empty, reverse, foldl, foldr, concat, flatten, any, all, sort, sortBy, sum, product, even, odd, lt, gt, eq, lteq, gteq, mean, maximum, minimum, zip, zipWith, keys, values, pairsToObj, objToPairs, listsToObj, objToLists, extend, extendAll, pick, omit, compose, curry, autoCurry.


id, kind of does nothing. a → a
id('foo')   // 'foo'
id([])      // []

isType. You can check if a variable has a certain type with this. "a" → a → Boolean
isType('Undefined', undefined)  // true
isType('Boolean', false)        // true
isType('Number', 3)             // true
isType('String', 'foo')         // true
isType('Object', {})            // true
isType('Array', [])             // true

replicate. Creates an array with however many items of whatever you want. Number → a → [a]
replicate(3, 'foo')   // ['foo', 'foo', 'foo']
replicate(2, true)    // [true, true]

dot. Gets a property of the passed in object. "a" → {a:b} → b
dot('age', {name: 'Simon', age: 45})   // 45
dot('status', {status: false})         // false

pluck. Does the same as dot, but for an array of objects. "a" → [{a:b}] → [b]
pluck('age', [{name: 'Simon', age: 45}, {name: 'Bob', age: 12}])    // [45, 12]
pluck('status', [{status: false}])                                  // [false]

each. Your general each or forEach function. Nothing spectacular. Notice, that as it is, it will default to the browser's native forEach if it exists, in which case it will return undefined and not the array you passed in. Function → a → a
each(alert, [1, 2, 3])          // will call alert on each number
each(alert, {one: 1, two: 2})   // once more :(

map. A map function. Like each it works on both arrays and objects (their values). Function → a → a
map(function(x){ return x+1 }, [1, 2, 3])           // [2, 3, 4]
map(function(x){ return x+1 }, {one: 1, two: 2})    // {one: 2, two: 3}

filter. Function → a → a
function even(x) { return x%2 === 0 }
filter(even, [1, 2, 3, 4])                  // [2, 4]
filter(even, {one: 1, two: 2, three: 3})    // {two: 2}

reject. Opposite filter. Function → a → a
reject(even, [1, 2, 3, 4])                  // [1, 3]
reject(even, {one: 1, two: 2, three: 3})    // {one: 1, three: 3}

partition. Kind of like filter and reject in one. It returns an array with two items: first one of filtered items, second of reject items. Fucntion → a → [a, a]
partition(even, [1, 2, 3, 4])                   // [[2, 4], [1, 3]]
partition(even, {one: 1, two: 2, three: 3})     // [{two: 2}, {one: 1, three: 3}]

find. Finds the first value that matches your function and returns that. Function → a → a
find(even, [1, 2, 3, 4])                  // 2
find(even, {one: 1, two: 2, three: 3})    // 2

head. Returns the "head" or first value of a list. [a] → a
head([1, 2, 3, 4])    // 1

tail. Returns the "tail" or everything but first value of a list. [a] → [a]
tail([1, 2, 3, 4])    // [2, 3, 4]

last. Returns the "last" or last value of a list. [a] → a
last([1, 2, 3, 4])    // 4

initial. Returns the "initial" part of a list, or everything but the last value. [a] → a
initial([1, 2, 3, 4])    // [1, 2, 3]

take. Takes n number of values from a list. Number → [a] → [a]
take(2, [1, 2, 3, 4])    // [1, 2]

drop. Drops n number of values from a list and returns the rest. Number → [a] → [a]
drop(2, [1, 2, 3, 4])    // [3, 4]

takeWhile. Copies your array until the values no longer returns truthy from your applied function. Function → [a] → [a]
takeWhile(even, [2, 4, 0, 3, 4, 2])      // [2, 4, 0]

dropWhile. Drops the values of a list until they no longer return truthy from your function. From that, it copies your array. Function → [a] → [a]
dropWhile(even, [2, 4, 0, 3, 4, 2])      // [3, 4, 2]

span is to takeWhile and dropWhile what partition is to filter and reject. Function → [a] → [[a], [a]]
span(even, [2, 4, 0, 3, 4, 2])       // [[2, 4, 0], [3, 4, 2]]

breakList breaks a list into two where the function returns truthy. Function → [a] → [[a], [a]]
breakList(even, [1, 3, 2, 4, 0, 3])       // [[1, 3], [2, 4, 0, 3]]

empty checks if an array or object is empty. a → Boolean
empty([])           // true
empty({})           // true
empty({one: 1})     // false

reverse returns a reversed shallow copy of the list. [a] → [a]
reverse([1, 2, 3])   // [3, 2, 1]

foldl is your standard reduce function. Function → a → [a] → a
function add(x, y) { return x + y }
foldl(add, 7, [1, 2, 3])      // 13

foldr is your standard reduceRight function. Function → a → [a] → a
function subtract(x, y) { return x - y }
foldl(subtract, 7, [1, 2, 3])      // 1

concat takes a list of lists and concatenates them. Notice it only does this one level down. [[a],[a]] → [a, a]
concat([[1, 2, 3], ['a', 'b']])      // [1, 2, 3, 'a', 'b']

flatten takes a list of lists and flattens them. [[a]] → [a]
flatten([ 1, [ [2], 3 ], [4, [[5]]], [{one: 1}, "hello"] ])      // [1, 2, 3, 4, 5, {one: 1}, "hello"]

any can tell you if any values in a list returns truthy from a function. Function → [a] → Boolean
any(even, [1, 2, 3])      // true
any(even, [1, 3, 77])     // false

all can tell you if all values in a list returns truthy from a function. Function → [a] → Boolean
all(even, [1, 2, 3])      // false
all(odd, [1, 3, 77])      // true

sort sorts an array of numbers. (Have a look at the implementation, it is highly subjective what you will expect this to do). [Number] → [Number]
sort([2, 12, 1])       // [1, 2, 12]

sortBy sorts an array according to a provided function. Function → [a] → [a]
sortBy(function(a, b) { return b-a }, [2, 12, 1])       // [12, 2, 1]

sum. Sums up numbers in an array. [Number] → Number
sum([3, 4, 5])           // 12

product. [Number] → Number
product([3, 4, 5])           // 60

even. Checks if a number is even. Number → Boolean
even(2)      // true

odd. Checks if a number is odd. Number → Boolean
odd(3)      // true

lt. "Less than". Notice that the arguments are in "reversed" order so they make more sense when curried. Number → Number → Boolean
lt(3, 2)                                  // true

// Curried
var lt3 = curry(lt, 3);
lt3(2)                                    // true
filter(lt3, [1, 5, 3, 9, 0, 2, 4, 2])     // [1, 0, 2, 2]

// Or better yet, redefine `lt` with autoCurry for better reuse
lt = autoCurry(lt);
listOfNum = [1, 5, 3, 9, 0, 2, 4, 2];     // Just because we will need it
filter(lt(3), listOfNum)                  // [1, 0, 2, 2]
filter(lt(5), listOfNum)                  // [1, 3, 0, 2, 4, 2]


// Let's go crazy
var filterLt = autoCurry(function(n, xs) {
  return filter(lt(n), xs);               // assuming `lt` is still autoCurried
});

// Here we call `filterLt` twice with one argument each
filterLt(3)(listOfNum)                    // [1, 0, 2, 2]

// Here we call `filterLt` once with two arguments
filterLt(5, listOfNum)                    // [1, 3, 0, 2, 4, 2]

gt. "Greater than" Number → Number → Boolean
gt(3,4)      // true

eq. "Equal" (This actually compares any two values using ===) Number → Number → Boolean
eq(3,3)      // true

lteq. "Less than, Equal" or <= Number → Number → Boolean
lteq(3,2)      // true

gteq. "Greather than, Equal" or >= Number → Number → Boolean
gteq(3,3)      // true

mean. Finds the mean of the numbers in a list. [Number] → Number
mean([1, 2, 3, 4])     // 2.5

maximum finds the highest number in a list. [Number] → Number
maximum([6, 7, 2, 3])     // 7

minimum finds the lowest number in a list. [Number] → Number
minimum([6, 7, 2, 3])     // 2

zip takes two arrays and "zips" them together. [a] → [b] → [[a, b]]
zip(['a', 'b', 'c'], [1, 2, 3])    // [['a', 1], ['b', 2], ['c', 3]]

zipWith takes a function and two arrays and zips them together using that function. Function → [a] → [a] → [a]
function add(x, y) { return x+y }
zipWith( add, [1, 2, 3], [7, 8, 9])     // [8, 10, 12]

keys returns an array with the keys of an object. {a:b} → [a]
keys({one: 1, two: 2, three: 3})     // ['one', 'two', 'three']

values returns an array with the values of an object. {a:b} → [b]
values({one: 1, two: 2, three: 3})     // [1, 2, 3]

pairsToObj. Takes an array of arrays with two values and turns them into an object. [[a, b]] → {a:b}
pairsToObj([['a', 1], ['b', 2], ['c', 3]])     // {a: 1, b: 2, c: 3}

objToPairs does the opposite as above. {a:b} → [[a, b]]
objToPairs({a: 1, b: 2, c: 3})     // [['a', 1], ['b', 2], ['c', 3]]

listsToObj takes two lists and returns an object with one arrays' values as keys and the other as the values. [a] → [b] → {a:b}
listsToObj(['a', 'b', 'c'], [1, 2, 3])     // {a: 1, b: 2, c: 3}

objToLists does the opposite as above. {a:b} → [[a], [b]]
objToLists({a: 1, b: 2, c: 3})     // [['a', 'b', 'c'], [1, 2, 3]]

extend takes two objects and "extends" them to a shallow clone. Because it takes a fixed number of arguments, it is easy to curry, whereas extendAll is a bit tricky. {a} → {a} → {a}
extend({one: 1, two: 2, three: 3}, {three: 33, four: 44})    // {one: 1, two: 2, three: 33, four: 44}

extendAll takes two or more objects and returns one object with the keys and values from all the others. (The copy is shallow) Because it takes a variable number of arguments, this function is a bit tricky to curry. {a} → {a} → ... → {a}
extendAll({one: 1, two: 2, three: 3}, {three: 33, four: 44}, {four: 'f', five: 'g'})
// {one: 1, two: 2, three: 33, four: 'f', five: 'g'}

pick returns an object with the keys you asked for. [a] → {a} → {a}
pick(['one', 'three'], {one: 1, two: 2, three: 3})     // {one: 1, three: 3}

omit. Opposite pick. [a] → {a} → {a}
omit(['one', 'three'], {one: 1, two: 2, three: 3})     // {two: 2}

compose. Composes functions together. Takes an optional number of arguments, so this can't be curried. Perhaps ironically, this function becomes a lot more powerful when other functions are curried. Function → Function → ... → Function
function getName(o) { return o['name'] }
var alertName = compose(alert, getName)
alertName({name: 'Simon', age: 45})     // alerts 'Simon'

// If `dot` is autoCurried (dot = autoCurry(dot);) then
var alertName2 = compose(alert, dot('name'));
alertName2({name: 'Simon', age: 45})    // alerts 'Simon'

curry. Returns a curried version of a function. This is not really meant to be used as is, but rather autoCurry is dependent on it. Function → a → ... → Function
function add(x, y) { return x+y }
var add1 = curry(add, 1)
add1(2)                             // 3
add1(5)                             // 6

autoCurry. Takes a function and an optional number, and returns a curried version of that function. The number, if applied, tells autoCurry how many arguments it needs to be fully applied. (if not applied it will deduct it using fn.length. I personally define almost all my function inside this function. Function → (Number →) Function
var cAdder = autoCurry( function(x, y) { return x+y } );

var add1 = cAdder(1);
add1(2)                             // 3
add1(5)                             // 6

var add5 = cAdder(5);
add5(2)                             // 7
add5(5)                             // 10

applyl. Takes a function and a list of arguments, and returns a function that waits for the last arguments. This function is a bit like curry, except curry will wait for all arguments to arrive, while this will run the function next time called, no matter what Function → [a] → Function
var calc = function(x, y, z) { return (x+y)*z };
var times3 = applyl(calc, [1, 2]);

times3(4)   // 12
times3(7)   // 21
times3()    // NaN


var isBool = applyl(isType, ['Boolean']);

isBool(false)     // true
isBool('Hello')   // false

applyr. Takes a function and a list of arguments, and returns a function that waits for the "first" arguments. Function → [a] → Function
var calc = function(x, y, z) { return (x+y)*z };
var add1AndMultiplyBy2 = applyr(calc, [1, 2]);

add1AndMultiplyBy2(3)   // 8
add1AndMultiplyBy2(21)  // 44