slapdash

Slapdash is a lightweight utility JavaScript utility belt, inspired heavily by lodash

Usage no npm install needed!

<script type="module">
  import slapdash from 'https://cdn.skypack.dev/slapdash';
</script>

README

Slapdash

What?

Codeship Status for Slapdash

Slapdash is a lightweight JavaScript utility belt, inspired heavily by lodash, with the following goals:

  • Perform as quickly as possible
  • Be as small as possible
  • Be compatible with Internet Explorer 8 and up
  • Protect from browser native overrides

Slapdash

Slapdash's prodigious speed allows him to do everything quickly—which he likes, because anything else would require effort.

-- Transformers Wiki

Why?

  • IE8/9 are a distant memory for most web developers, but unfortunately for some of us they remain a waking nightmare. Slapdash aims to change this.
  • Underscore is bloated. Lodash is overweight. Slapdash is svelte.
  • We don't all have control over our execution environment. Maybe you're writing script for deployment on 3rd party sites - maybe that 3rd party decides to override Function::bind with something incompatible? Don't worry. Slapdash has your back.

How?

  • IE8 is crap by modern standards. I mean, it doesn't even have Object.keys!. So, Slapdash featurefills missing native methods without touching any prototypes.
  • By not implementing the full lodash/underscore API, a lot of cruft can be removed. This includes, for example, supporting strings in place of callbacks in map/each/filter etc.
  • Where possible, slapdash uses browser native methods to boost performance. However, the first time it attempts to get a native method, it will make sure that it is the one it expected. If another script has tampered with the prototype (or doesn't provide one at all), we fall back to our own (small, performant) implementation.

Usage

npm install slapdash

You can then import the library like this:

var _ = require('slapdash')

// or in ES6

import _ from 'slapdash'

If you find yourself only using a single method, you can import it directly from the source. However, I strongly recommend against this approach, as most bundling tools (such as webpack/browserify) add additional closures around each file you import, which both add cruft and work for the browser.

var map = require('slapdash/src/map')

// or

import map from 'slapdash/src/map'

Bundle Size

Bundle Size (Kb)
Normal bundle 7.8
Minified (uglifyjs -c -m toplevel) 3.7
Minified + Gzipped 1.3

Design Decisions

One of the most problematic aspects of using lodash's common methods (like map, each, reduce, et. al.) is that they can operate on either arrays or objects, and that they accept callbacks in the form of strings and objects, as well as simply functions. This means that:

  • There are subtleties to the behavior of each method depending on the types of the operands.
  • Reading code which uses these methods can be difficult to reason about. Unless the operand in question is declared near to where it is used (or there is an assertion/invariant to validate the type), it can be difficult to determine what type it actually is.

As such:

  • Slapdash's 'collection' methods are split into two sets - array and object. Each of lodash's collection methods will be implemented to support arrays only, with an object-specific version provided separately.
  • Callbacks supplied to these methods may only be functions. For most common use cases involving a non-function callback, an alternative will be provided.

API Reference

bind(method, context, arguments...)

This returns a wrapper function around method, passing context as this, and optionally passing one or more arguments. This is a featurefill of the Function.prototype.bind method.

assign(target, ...sources)

Depending on the browser (and/or bad polyfills), this is either Object.assign or an equivalent featurefill.

Notes

  • This does not support the optional callback and context methods from lodash.

keys(object)

Returns the own property keys of object. This is a featurefill around Object.keys.

values(object)

Returns the own property values of object. This is a featurefill around Object.values.

slice(array, start, end)

Returns a subset of array, from the index start to the index end. Negative values are supported for start/end. This is a featurefill around Array.prototype.slice.

each(array, callback, context)

This is a wrapper around Array.prototype.forEach.

Notes

  • Returning false will not halt execution of this loop, unlike lodash.
  • This only supports arrays, not objects. Use objectEach instead.
  • Callback must be a function. Objects or strings are not supported.

objectEach(object, callback, context)

Iterates over the properties of object, calling callback on each. This is similar to passing an object to lodash's each method.

Notes

  • Returning false will not halt execution of this loop, unlike lodash.
  • This only supports objects, not arrays. Use each instead.
  • Callback must be a function. Objects or strings are not supported.

map(array, callback, context)

This is a wrapper around Array.prototype.map.

Notes

  • This only supports arrays, not objects. Use objectMap instead.
  • Callback must be a function. Objects or strings are not supported. For string support, use pluck.

objectMap(object, callback, context)

Behaves like map, but operates on the values of an object. Returns a new object, with the same keys, but with the values passed through callback.

Lodash's default behavior returns an array of mapped-over values - to get an array back from objectMap, use objectMap.asArray(...)

Notes

  • This only supports objects, not arrays. Use map instead.
  • Callback must be a function. Objects or strings are not supported.

find(array, callback, context)

Where available, this will call Array.prototype.find. Otherwise, this provides its own implementation.

  • This only supports arrays, not objects. There is no objectFind, as it's really not that useful.
  • Callback must be a function. Objects or strings are not supported.

pluck(array, key)

Returns a new array, containing the key attribute from each member of array.

reduce(array, callback, initialValue)

This is just a wrapper around Array.prototype.reduce.

Notes

  • This only supports arrays, not objects. Use objectReduce instead.
  • Callback must be a function. Objects or strings are not supported.

objectReduce(object, callback, initialValue)

This implements the object behavior of lodash's reduce method.

Notes

  • This only supports objects, not arrays. Use reduce instead.
  • Callback must be a function. Objects or strings are not supported.

invoke(array, methodName)

Returns a new array, containing the result of calling each member of array's methodName method.

Notes

  • This only supports arrays, not objects.

filter(array, predicate, context)

Returns a new array containing the items in array for which predicate returns true. If context is supplied, it will be passed to callback as this.

Notes

  • Only supports arrays, not objects. If there's demand for an objectFilter method, raise an issue/PR.

indexOf(array, item)

This is a wrapper around Array.prototype.indexOf.

isMatch(object, matchAgainst)

Returns true if and only if every property of the object matchAgainst exists in object and is strictly equivalent (===).

matches(matchAgainst)

Returns a function which accepts an object as a parameter, and returns true if every property of the object matchAgainst exists in object and is strictly equivalent (===).

This can be handy in replicating _.findWhere, which slapdash doesn't implement. For example:

var joe = _.find(users, _.matches({ id: 4567 }))

identity(value)

Returns value. Useless by itself, but useful as a callback to filter, find and others.

not(value)

Returns true if value is falsey, otherwise returns false. Useful as a callback to find.

get(object, path)

Given a path such as 'x.y.z', this will return object.x.y.z. Array elements can also be accessed, by treating the array index like an object key, e.g. get(object, 'a.b.c.3').

set(object, path, value)

Given a path such as 'x.y.z', this will set object.x.y.z = value. Like get, this also supports array indices.

unique(array)

Returns a copy of array, without duplicate values (using strict equivalence, i.e. ===).

pick(object, keys)

Returns a copy of object with only the keys that exist in keys.

some(array, callback, context)

Check if some of the items in the array match the predicate, you need to pass a predicate function and an optional context.

This is essentially a featurefill of Array.prototype.some.

_.some([1, 2, 3], _.identity) // true
_.some([false, true, false], _.identity) // true
_.some([false, false, false], _.identity) // false
_.some([-1, 0, 1], isPositive) // true

every(array, callback, context)

Check if all of the items in the array match the predicate, you need to pass a predicate function and an optional context.

This is essentially a featurefill of Array.prototype.every.

isObject(value)

Returns true if value is an object or a function, and not null.

isArray(value)

Returns true if value is an array. Does not return true for array-like objects (i.e. arguments et. al.).

This is essentially a featurefill of Array.isArray.

debounce(func, wait, immediate)

Returns a debounced version of func, which, regardless of how many times it is called, will only actually call func at most once every wait milliseconds.

If immediate is truthy, then func will be called immediately on the first call - otherwise it will be delayed by wait milliseconds.

If you were looking for...

findWhere(array, object)

You can use matches to create a callback for find:

_.find(array, _.matches(object))