Programming with

LO-DASH

What is Lodash?

Lodash is an utility library designed for everyday use. This is the missing toolkit for Javascript to start being productive right away.

Lodash is for Javascript what jQuery is for DOM.

Example: map over an array

var arr = ["John", "Marc", "Louie"];
var result = [];
for (var i = 0; i < arr.length; i++) {
  result.push(arr[i].toUpperCase());
}
return result;
var arr = ["John", "Marc", "Louie"];
return arr.map(function(name) {
    return name.toUpperCase();
});

// What if arr is undefined ? Error

ES5.1 version

Old school version

With Lodash

var arr = ["John", "Marc", "Louie"];
return _.map(arr, _.method('toUpperCase'))

// If arr is undefined, it will return undefined

So what's the difference?

Lodash can map over any iterable

return _.map("Hello", function (letter) {
    return letter + "-"
])

// return "H-e-l-l-o"
var obj = {name: "John", city: "Tel Aviv"}
return _.map(obj, function (key, value) {
    var res = {};
    res[key.toUpperCase()] = value.toLowerCase();
    return res;
])

// return { NAME: "john", CITY: "tel aviv" }

Strings

Objects

Functional programming style

var arr = [{name: 'Foo'}, {name: 'Bar'}]
return _.map(arr, 'name')

// return ['Foo', 'Bar']
var arr = ["John", "Mike"]
return _.map(arr, _.method('toUpperCase'))

// return ["JOHN", "MIKE"]

Get the name property of a list of objects

Use with native functions

var arr = ["data_feeds", "first_name"]
return _.map(arr, _.toCamelCase)

// return ["dataFeeds", "firstName"]

Use with lodash functions

What's inside?

Collection Utilities

  • each: iterate over a collection
  • map: execute a function over all elements of a collection
  • filter/reject: filter out elements satisfying a predicate
  • reduce: run a function over all elements and accumulate the results
  • some/every: check that there is at least one element/all elements satisfying a predicate
  • find/findIndex: find an element in an array
  • groupBy/countBy: group elements over a predicate
  • pluck: "pluck" properties of objects into an array

What's inside?

Array Utilities

  • first/last/slice: Get the first/last n elements
  • compact: eliminate null elements
  • take/drop: Take/Drop the first elements
  • union/intersection: merge two arrays and take the union or intersection of their elements
  • difference: merge two arrays and take the difference between their elements.
  • remove: remove elements
  • flatten: flatten internal arrays of an array
  • fill: create an array and fill it with some data

What's inside?

Object Utilities

  • extend/merge: Extend an object with another object(s)
  • pick: return an object with only the "picked" properties
  • omit: return an object without the "omitted" properties
  • get: Get a property (including nested properties)
  • set: Set a property (including nested properties)
  • keys/values: return an array of the properties/values of the object.
  • methods: return only methods of the object
  • result/method: call a method on an object and return its result
  • fill: create an array and fill it with some data

What's inside?

String/Number Utilities

  • camelCase/snakeCase: Convert a string
  • kebabCase: Convert to an-hyphenated-string
  • capitalize: Capitalize the first letter
  • startsWith/endsWith: Check whether the string starts/ends with a pattern
  • set: Set a property (including nested properties)
  • trunc: truncates a string to a certain length
  • repeat: repeat a string
  • words: split a sentence into an array of "words"
  • parseInt: lodash version of the method
  • random: get a random number

What's inside?

Other Utilities

  • isObject, isArray, isFunction, isNumber, isString...
  • clone, cloneDeep: Clone an object
  • toArray: convert an object into an array
  • property: create a function (getter) that returns a property of any given object
  • constant: create a function that always returns the same value
  • range: create a range of numbers
  • uniqueId: generates a unique id
  • now: Returns the timestamp of now
  • template: Create a templated string

Functional style

  • Lodash embraces functional style coding
  • Features:
    • Function composition
    • Function deferring/delaying
    • Partial applications/currying
    • Delegation/Binding/Unbinding
// Execute last then upperCase
var upperLast = _.compose(_.method('toUpperCase'), _.last)
upperLast(["John", "Mike", "Louie"];) // 'LOUIE'

// Partial right will bind the last argument of map (the callback)
// with the provided callback, here snakecase
var snakeCaseArray = _.partialRight(_.map, _.snakeCase)
snakeCaseArray(['I love boats', 'I love planes']) // ['i_love_boats', 'i_love_planes']

// Compose the compositions
var upperLastSnakeCase = _.compose(upperLast, snakeCaseArray)
upperLastSnakeCase(['I love boats', 'I love planes']); // 'I_LOVE_PLANES'

Chaining style

  • Lodash makes it easier to just chain methods
var array = [{name: 'John'}, {name: 'Mike'}, {name: 'Jim'}, 10]
_(array)
.filter(item => _.isObject(item))
.pluck('name')
.map(_.capitalize)
.groupBy(item => _.first(item))
.value()

// return { 'J': 2, 'M': 1 }

Using with Angular

Manage models

function MyAngularService($http) {

    function MyModel(data) {
        // defaults
        this.name = '';
        this.description = '';

        // Prefill with data
        _.extend(this, data) 

        // strip out name from the label objects
        this.labels = _.pluck(this.labels, 'name') 
    }

    this.serialize = function (object) {
        // Only send out the relevant fields
        return _.pick(object, ['name', 'description', 'labels']);
    }

}

Using with Angular

Manage collections

function MyAngularController($scope, dataFeeds) {

    $scope.dataFeeds = _(dataFeeds)
                        .compact()
                        .filter(dataFeed => dataFeed.isActive)
                        .sortBy('name')
                        .groupBy('status')
                        .value();

    $scope.get = function (name) {
        return _.where($scope.dataFeeds, {name: name})
    }

    $scope.delete = function (id) {
        return _.remove($scope.dataFeeds, {id: id})
    }    

    $scope.search = function (query) {
        return _.filter($scope.dataFeeds, _.partialRight(_.contains, query))
    }

    $scope.refresh = function (newDataFeeds) {
        return _.union($scope.dataFeeds, newDataFeeds)
    }

}

Using with Angular

Create filters

function removeDuplicates() {
    return function(collection, property) {
        return _.unique(collection, property);
    }
}

function camelCaseKeys() {
    return function(object) {
        return _.mapKeys(object, (v, k) => _.camelCase(k));
    }
}
<div ng-repeat="item in items | removeDuplicates:'name'">{{ item }}</div>

<pre>{{ item | camelCaseKeys | json }}</pre>

Using with Angular

Example: pagination

<div ng-repeat='user in users | slice:(page * amountPerPage):((page + 1) * amountPerPage)'>
  {{user.name | capitalize }}
</div>
<button ng-click='page = page - 1'>Previous</button>
<span>Page {{ page }} of {{ users | size }}</span>
<button ng-click='page = page + 1'>Next</button>

Using with Angular

Application: debouncing watchers

// The watcher function will be triggered only once every 1000 ms
$scope.$watch('property', _.debounce(function(newVal, oldVal) {
    // do something
}, 1000));

Application: Run once watchers

// The watcher function will be triggered only once 
$scope.$watch('property', _.once(function(newVal, oldVal) {
    // do something
}, 1000));

Using with Angular

Application: Getting a heavily nested property

// Before
function getFirstDataFeedTemplateName(dfs) {
    return dfs && dfs[0] && dfs[0].template && dfs[0].template.name;
}

allNames = allNames.map(function(df) {
    return df && df.template && df.template.name;
})

// After
function getFirstDataFeedTemplateName(dfs) {
    return _.get(dfs, '[0].template.name')
}

allNames = _.map(dfs, _.property('template.name'))

Using with Angular

Application: Replace for loops

// Before
var objects = [];
for (var i = 0; i < 100; i++) {
    objects.push(new MyObject());
}

// After
var objects = _.range(100).map(i => new MyObject());

Using with Angular

Application: Interpolate strings

// Before
var myData = {name: 'Foo'}
var myString = 'My name is ' + myData.name + '!';

// After
var myString = _.template('My name is <%= name %>!')(myData)

// PS: ES2015 template strings are way better

Questions?

Lodash

By Elior Boukhobza

Lodash

  • 1,471