The Emacs Series Exploring dash.el

0
3549

dash.el is a list library for Emacs released under the GNU General Public License v3.0. It has been written by Magnar Sveen and the latest release is v2.15.0. The source code is available at https://github.com/magnars/dash.el. In this first part of the series of articles on dash.el, we will explore functions on map transformations, sublist selection, list operations and reductions.

Before going into any details about dash.el, let’s first get the installation done.

Installation
The dash.el package is available in Milkypostman’s Emacs Lisp Package Archive (MELPA) and in the Marmalede repo. You can install the package using the following command in GNU Emacs:

M-x package-install dash

If you want the function combinators, then you should also install the following:

M-x package-install dash-functional

The other method of installation is to copy the dash.el source file to your Emacs load path and use it.
In order to get syntax highlighting of dash functions in Emacs buffers, you can add the following to your Emacs initialisation settings:

(eval-after-load ‘dash ‘(dash-enable-font-lock))

Usage
The API functions available in dash.el are prefixed with a dash
(‘-‘). Let us first explore the map transformation functions on lists.

Maps
The ‘-map’ function applies a given function to each element in a list. In the following example, the ‘square’ function squares each element in the list:

(-map function list) ;; Syntax

(defun square (n) (* n n))
(-map 'square '(2 3 4))
(4 9 16)

The ‘-map-when’ function takes three arguments — a predicate function, an application function, and a list. All the elements of the list that match the predicate function are transformed using the application function. If an element does not satisfy the predicate function, the value is unchanged. The ‘even?’ function takes a number as the input and determines if it is an even or odd number. This can be used as the predicate function, along with the square function as the application function to the list (2 3 4) as shown below:

(-map-when predicate application list) ;; Syntax

(defun even? (num) (= 0 (% num 2)))
(-map-when 'even? 'square '(2 3 4))
(4 3 16)

The ‘-map-first’ function replaces the first element in the list that matches the predicate function and executes the application function on the element. Similarly, the ‘-map-last’ function replaces the last element in the list that satisfies the predicate function by executing the application function on the element. A couple of examples are given below:

(-map-first predicate application list) ;; Syntax
(-map-last predicate application list) ;; Syntax

(-map-first 'even? 'square '(2 3 4))
(4 3 4)

(-map-last 'even? 'square '(2 3 4))
(2 3 16)

The ‘-annotate’API applies a function to each element of the list, and pairs the result to the unmodified list. An example is shown below:

(-annotate function list) ;; Syntax

(-annotate 'length '(("a" "b" "c") ("foo" "bar")))
((3 "a" "b" "c") (2 "foo" "bar"))

The ‘-splice-list’ function takes three arguments — a predicate function, a new list and the input list. It splices the input list at places where the elements match the predicate function with the new list. In the following example, the input list ‘(x :symbol y)’ is spliced between x and y as the predicate function checks for a keyword, and replaces it with the new list ‘(a b c)’.

(-splice-list predicate new-list list) ;; Syntax

(-splice-list 'keywordp '(a b c) '(x :symbol y))
(x a b c y)

The ‘-mapcat’ function takes a function and a list, applies the function to each element of the list, and concatenates the results. For example:

(-mapcat function list) ;; Syntax

(--mapcat (list '* it) '(9 8 7))
(* 9 * 8 * 7)

Sublist selection
The ‘-filter’ function returns only those elements of a list that match the predicate function passed to it as an argument:

(-filter predicate list) ;; Syntax

(-filter 'even? '(1 2 3 4))
(2 4)

The ‘-remove’ function removes those elements that match the predicate function from the input list, and returns a new list as shown below:

(-remove predicate list) ;; Syntax

(-remove 'even? '(1 2 3 4))
(1 3)

If you only want to remove the first element or last element from the input list that matches the predicate function, then you can use the ‘-remove-first’ and ‘-remove-last’ functions respectively. A couple of examples are given below for reference:

(-remove-first predicate list) ;; Syntax
(-remove-last predicate list) ;; Syntax

(-remove-first 'even? '(1 2 3 4))
(1 3 4)

(-remove-last 'even? '(1 2 3 4 5 6 7 8 9 0))
(1 2 3 4 5 6 7 8 9)

The ‘-non-nil’ function returns all the elements from the input list that are not nil. For example:

(-non-nil '(1 2 3 nil 4 5 6 nil))
(1 2 3 4 5 6)

The ‘-slice’ function takes an input list as an argument and returns all the elements between a ‘from’ and ‘to’ position. The list index starts from zero, and an optional step can also be given. In the following example, the second element from the input list is returned.

(-slice list from to step) ;; Syntax

(-slice '(1 2 3 4 5) 2 3)
(3)

The ‘-take’ function returns a new list with the first N (number) elements from the input list. If N is greater than the number of elements in the list, then the entire list is returned. For example:

(-take N list) ;; Syntax

(-take 3 '(1 2 3 4 5))
(1 2 3)

The ‘-take-last’ function returns the last N items from the input list.

(-take-last N list) ;; Syntax

(-take-last 3 '(1 2 3 4 5))
(3 4 5)

The ‘-drop’ function removes the first N items from the input list, and returns the rest of the list (tail), while the ‘-drop-last’ function removes the last N items from the list and returns the rest of the list. A couple of examples are shown below:

(-drop N list) ;; Syntax
(-drop-last N list) ;; Syntax

(-drop 3 '(1 2 3 4 5))
(4 5)

(-drop-last 3 '(1 2 3 4 5))
(1 2)

The ‘-take-while’ function returns the input list until each element satisfies the predicate function. The function usage is demonstrated with the following examples:

(-take-while predicate list) ;; Syntax

(-take-while 'even? '(1 2 3))
nil

(-take-while 'even? '(2 3 4))
(2)

The ‘-drop-while’ function removes the elements from the input list as long as the predicate function matches, and returns the rest of the list. A couple of examples are shown below:

(-drop-while predicate list) ;; Syntax

(-drop-while 'even? '(1 2 3 4))
(1 2 3 4)
(-drop-while 'even? '(2 3 4 5))
(3 4 5)

The elements of a list at specific indices can be returned using the ‘-select-by-indices’ function. It takes a list of indices as an argument and an input list. For example:

(-select-by-indices indices list) ;; Syntax

(-select-by-indices '(0 2 3) '("a" "b" "c" "d" "e"))
("a" "c" "d")

A matrix or table can be represented as a list of lists. The elements in a specific column can be returned as a list using the ‘-select-column’ function. An example is given below:

(-select-column columns table) ;; Syntax

(-select-column 1 ‘((1 2 3) (4 5 6) (7 8 9)))
(2 5 8)
3.3.

List to list operations
The ‘-keep’ function takes a function as an argument and applies the former to each element in the list. It then returns a new list with those elements that return a non-nil value for the function application. For example:

(-keep function list) ;; Syntax

(-keep 'cdr '((7 8 9) (7 8) (7)))
((8 9) (8))

If you want to concatenate lists together, you can use the ‘-concat’ function as shown below:

(-concat '(1) '(2 3) '(4))

(1 2 3 4)

Nested lists can be flattened to a single list using the ‘-flatten’ function. You can also specify the level of the nested list to flatten using the ‘-flatten-n’ function. A couple of examples are provided below to illustrate the function’s use:

(-flatten list) ;; Syntax
(-flatten-n N list) ;; Syntax

(-flatten '((5)))
(5)
(-flatten '((1 (2 3) (((4 (5)))))))
(1 2 3 4 5)

(-flatten-n 1 '((1 2) ((3 4) ((5 6)))))
(1 2 (3 4) ((5 6)))

The ‘-replace’ function replaces old items in the list with the new item. For example:

(-replace old new list) ;; Syntax

(-replace 1 “one” ‘(1 2 3 4 5))
(“one” 2 3 4 5)

The ‘-replace-first’ function replaces the first occurrence of the old item in the list with the new item, while, the ‘-replace-last’ function replaces the last occurrence of the old item with the new item. A couple of examples to demonstrate the function application are given below:

(-replace-first old new list) ;; Syntax
(-replace-last old new list) ;; Syntax

(-replace-first 1 "one" '(1 2 1 2 1 2))
("one" 2 1 2 1 2)

(-replace-last 1 "one" '(1 2 1 2 1 2))
(1 2 1 2 "one" 2)

The ‘-insert-at’ function returns a list with the input element inserted at the specified position in the argument list. For example:

(-insert-at position element list) ;; Syntax

(-insert-at 1 2 '(1 3 4))
(1 2 3 4)

The ‘-replace-at’ function replaces an element in the specified position with the input element for the given argument list. In the following example, the element at index 1 is replaced with the number ‘2’.

(-replace-at position element list) ;; Syntax

(-replace-at 1 2 '(1 1 3 4))
(1 2 3 4)

The ‘–update-at’ function takes three arguments — a position, a function and an input list. It takes the element at the position, applies the function on the element replacing it with the function result, and returns the list. For example:

(-update-at position function list) ;; Syntax

(--update-at 1 (length it) '("thank" "you" "very" "much"))
("thank" 3 "very" "much")

The ‘-remove-at’ function returns a list with the element at the given position removed from the argument list. An example is given below:

(-remove-at position list) ;; Syntax

(-remove-at 2 '(1 2 3 4 5))
(1 2 4 5)

Reductions
In this section, we will go through functions that reduce a list to a single value. This is a reduction or merge function.
The ‘-reduce-from’ function takes three arguments — a function, an initial value and a list. It starts by applying the function to the initial value and the first element of the list. The result then becomes the new initial value or accumulator. The function is then applied to this new value and the second element of the list, and so on. For example:

(-reduce-from function initial-value list) ;; Syntax

(-reduce-from ‘- 100 ‘(10 20 30))
40

The function application occurs in the following order for the above example:

((- 100 10) 20 30) ; 100 - 10 = 90
((- 90 20) 30) ; 90 - 20 = 70
(- 70 30) ; 70 - 30 = 40

The ‘-reduce-r-from’API is similar to the ‘-reduce-from’ function, except that the operation association occurs from the right-hand side instead of the left-hand side. An example is given below to illustrate the function application:

(-reduce-r-from function initial-value list) ;; Syntax

(-reduce-r-from ‘- 100 ‘(10 20 30)) 
-80

The function application evaluates in the following order for the above example:

((- 30 100) 10 20) ; 30 - 100 = -70
((- 20 (-70)) 10) ; 20 - (-70) = 90
(- 10 90) ; 10 - 90 = -80

The ‘-reduce’ function applies the function to the first two elements of the list, then applies the function to the previous result, and then to the third element of the list, and so on. For example:

(-reduce function list) ;; Syntax

(-reduce '- '(1 2 3 4))
-8

The function application evaluation order for the above example is as follows:

((- 1 2) 3 4) ; 1 - 2 = -1
((- (-1) 3) 4) ; (-1) - 3 = -4
(- (-4) 4) ; (-4) - 4 = -8

The ‘-reduce-r’ function is similar to the ‘-reduce’ function with the exception that it evaluates from right-to-left. An example to illustrate the function is given below:

(-reduce-r function list) ;; Syntax

(-reduce-r ‘- ‘(1 2 3 4))
-2

The function application evaluation order is as follows for the above example:

((- 3 4) 1 2) ; 3 - 4 = -1
((- 2 (-1)) 1) ; 2 - (-1) = 3
(- 1 3) ; 1 - 3 = -2

The ‘-count’ function takes a predicate function and a list as arguments. It counts the number of items that satisfy the predicate function. For example:

(-count predicate list) ;; Syntax

(-count ‘even? ‘(1 2 3 4))
2

The ‘-sum’ function simply returns the sum of the list. A few examples are shown below:

(-sum list) ;; Syntax

(-sum ‘())
0
(-sum ‘(1))
1
(-sum ‘(1 2 3 4))
10

The ‘-product’ function computes the product of the elements in a list. An example is given below:

(-product list) ;; Syntax

(-product ‘(1 2 3 4))
24

The ‘-min’ and ‘-max’ functions return the smallest and largest values from the items in a list. A couple of examples are shown below:

(-min list) ;; Syntax
(-max list) ;; Syntax

(-min '(5 2 3 4))
2

(-max '(1 2 3))
3

The ‘-min-by’ and ‘-max-by’ functions take a comparator function as an argument, and return the least and greatest elements in a list, based on the comparison function. A few examples are given below:

(-min-by comparator list) ;; Syntax
(-max-by comparator list) ;; Syntax

(-min-by '> '(5 2 3 4))
2

(-max-by '> '(5 2 3 4))
5

(--max-by (> (length it) (length other)) '((1 2 3) (4) (5 6)))
(1 2 3)

We will explore more functions from dash.el in the second part of this series of articles.

LEAVE A REPLY

Please enter your comment!
Please enter your name here