# Org tutorial on table lookup functions

## Table of Contents

## Introduction

Org provides three different functions for performing searches and data dependent calculations in tables. These functions can, among other things, be used to implement associative arrays, count matching cells, rank results, or group data. The following examples will hopefully help you in getting started with these functions.

## Associative array with unique keys

The most straightforward use of lookups is to treat part of an org table as an associative array: a key can be used to look up a corresponding value.

Say you are taking a trip to Scandinavia, and you want to keep track of how much money you have spent on the trip. You decide to convert all sums to euros. Before your trip you write down the following table of approximate currency rates.

#+TBLNAME: rates | currency | abbreviation | euros | |-----------------+--------------+-------| | euro | eur | 1 | | Norwegian krone | nok | 0.14 | | Swedish krona | sek | 0.12 | | US dollar | usd | 0.77 |

In what follows we will use the function `org-lookup-first`

and the
previous table `rates`

to automatically convert the sums in
different currencies to euros. The signature of function
`org-lookup-first`

looks as follows:

```
(org-lookup-first VAL S-LIST R-LIST &optional PREDICATE)
```

Assuming that `PREDICATE`

is `nil`

, in which case the default
predicate `equal`

is used, this function does a search for the first
instance of `VAL`

in `S-LIST`

and returns the a value from the
corresponding position in `R-LIST`

. In the table below, each sum is
assigned a currency abbreviation; a lookup is done in table `rates`

above in the second column for the corresponding abbreviation, and
then the corresponding rate is returned from the third column. For
each row only the first four columns need to filled; columns 5 and 6
are calculated automatically. Notice that an error results if the
key is not found: in the last row, an empty key is being searched
for.

| date | expense | sum | currency | rate | euros | |-------+------------------+------+----------+--------+--------| | 1.3. | flights | 324 | eur | 1 | 324 | | 4.6. | books and maps | 243 | usd | 0.77 | 187.11 | | 30.7. | rental car | 8300 | sek | 0.12 | 996. | | 2.7. | hotel | 1150 | sek | 0.12 | 138. | | 2.7. | lunch | 190 | sek | 0.12 | 22.8 | | 3.7. | fishing licenses | 1400 | nok | 0.14 | 196. | | 3.7. | gasoline | 340 | | #ERROR | #ERROR | #+TBLFM: $5='(org-lookup-first $4 '(remote(rates,@2$2..@>$2)) '(remote(rates,@2$3..@>$3)))::$6=$5*$3

## Multiple matches with preferred ordering

A common task for teachers is the assignment of exam grades from total marks. The starting point for such grading is a table with grade boundaries. Below is one such table, with the rows in increasing order of the lower bound required for a particular grade.

#+TBLNAME: grade-boundaries | lower bound | grade | |-------------+-------| | 0 | F | | 10 | D | | 20 | C | | 30 | B | | 40 | A |

We will use the function `org-lookup-last`

and the previous table
`grade-boundaries`

to assign grades to students based on their
marks. The signature of function `org-lookup-last`

is exactly like
the signature of `org-lookup-first`

:

```
(org-lookup-last VAL S-LIST R-LIST &optional PREDICATE)
```

However, this function does a search for the *last* match in
`S-LIST`

and returns the a value from the corresponding position in
`R-LIST`

. Here the idea of the lookup used in assigning the grade is
as follows. Say a student's exam result is 33 marks. We look for
the *last* row in the table for which the student's marks are greater
than or equal to the lower bound; in this case it is the row with
lower bound 30. The student's grade is the corresponding element from
the second column, in this case a B.

Thus, given the number of marks `VAL`

of a student, we find the last
row of the first column of table `grade-boundaries`

for which the
lower bound `S`

fulfils `(>= VAL S)`

. Thus we will use `>=`

as
`PREDICATE`

to perform the matching. Note that `VAL`

and `S`

are
assigned as arguments to the predicate in the order they are in the
signature of `org-lookup-last`

, where `VAL`

precedes `S-LIST`

. The
following table does the conversion from total marks to the final
grade. Notice the literal interpolation `L`

of table values into
the Elisp formula, which is needed because some values are numbers
and some are symbols.

| student | marks | grade | |---------+-------+-------| | X | 30 | B | | Y | 29 | C | | Z | 5 | F | | W | 55 | A | #+TBLFM: $3='(org-lookup-last $2 '(remote(grade-boundaries,@2$1..@>$1)) '(remote(grade-boundaries,@2$2..@>$2)) '>=);L

## Counting matching cells

The function `org-lookup-all`

can not be used by itself in a table
equation, because it returns a list of values. However, powerful
lookup tasks can be performed by combining the function with other
Elisp functions.

As a simple example consider counting the number of missing values
in a table. The signature of function `org-lookup-all`

is exactly
like the signatures of the other two lookup functions:

```
(org-lookup-all VAL S-LIST R-LIST &optional PREDICATE)
```

However, this function does a search for the *all* matches in
`S-LIST`

and returns the all corresponding values from the
corresponding positions in `R-LIST`

. As is the case with
`org-lookup-first`

and `org-lookup-last`

, if `R-LIST`

is `nil`

, then
the corresponding matching values of `S-LIST`

are returned
directly. Notice the use of the `E`

flag to retain empty fields in
the range. Also notice that in this case we are doing the lookup in
a true two-dimensional range, which is thus also possible

| group | round 1 | round 2 | |-------+---------+---------| | A | | 2.4 | | B | 4.7 | 11 | | C | | | | D | 5 | | | E | | 7.2 | | F | 3.2 | 4.3 | | G | | 4.4 | | H | | 8 | |-------+---------+---------| | total | missing | 7 | #+TBLFM: @>$3='(length(org-lookup-all "" '(@2$2..@-1$3) nil));E

## Ranking results

Another example application of `org-lookup-all`

is an automatic
ranking of results. In the table below, a larger total number of
marks is better. Notice that the Elisp expression also
automatically takes care of ties.

| group | marks | rank | |-------+-------+------| | A | 22 | 2 | | B | 22 | 2 | | C | 14 | 4 | | D | 28 | 1 | | E | 9 | 5 | #+TBLFM: $3='(+ 1 (length (org-lookup-all $2 '(@2$2..@>$2) nil '<)));N

## Frequency counts from raw data

A common situation in the analysis of data is the classification
(grouping) of raw data values for, e.g., visualisation. Often this
is done by counting the frequencies of observations within certain
bounds. The function `org-lookup-all`

, combined with other Elisp
functions, can be used to perform this task. This example also shows
how to construct more complicated lookup rules using multiple values
from a table.

Consider the following table with different results from different groups A-I.

#+TBLNAME: raw-data | group | result | |-------+--------| | A | 2.3 | | B | 4.2 | | C | 1.1 | | D | 3.6 | | E | 4.5 | | F | 2.4 | | G | 1.0 | | H | 2.3 | | I | 2.8 |

We will classify the results into different, mutually exclusive
classes. For example, the observations that will belong to the first
class are in the interval `[1, 1.9]`

(endpoints included). In order
to perform this classification, we define the following two-place
predicate function `in-interval`

. Notice that the first parameter of
this function is a pair whose first element is the lower bound and
second member the upper bound of the interval.

#+BEGIN_SRC emacs-lisp (defun in-interval (bounds el) (and (>= el (car bounds)) (<= el (cadr bounds)))) #+END_SRC

#+RESULTS: : in-interval

Using this predicate function, we can construct a table with class
boundaries and corresponding frequencies. Note that the first
argument to the function `org-lookup-all`

, which is then passed over
as the first argument to the predicate `in-interval`

, is the pair of
bounds.

| lower bound | upper bound | frequency | |-------------+-------------+-----------| | 1 | 1.9 | 2 | | 2 | 2.9 | 4 | | 3 | 3.9 | 1 | | 4 | 4.9 | 2 | #+TBLFM: $3='(length (org-lookup-all '($1 $2) '(remote(raw-data,@2$2..@>$2)) nil 'in-interval));N

## Conclusion

The org lookup functions can be used for a large number of different
data-dependent calculations. For example, the following spreadsheet
operations familiar to libreoffice or Excel users can be implemented
using them: `HLOOKUP`

, `VLOOKUP`

, `COUNTIF`

, `SUMIF`

and
`FREQUENCY`

. If you have other interesting examples of the use of
these functions, feel free to send them to the org mailing list and
we will be happy to add them on this page.