Expressions

Example expressions are 1 + 2, row / 5, @if row < 10 @then 0 @else row @endif. Expressions are called formulas in spreadsheets. They are a way of performing some calculation, generally as part of a larger transformation. For example, Calculate processes the expression once per row and shows the value, Filter processes the expression once per row and keeps only certain rows.

Expressions very often use the values of columns in them. Unlike spreadsheets, columns are referred to by name (rather than cell references like B6). Columns can be referred to either as a single value on the current row, or as a list of all the values in the column.

Columns

Most expressions use a column. This might be to use a column value to filter out some rows (e.g. Sales > 0, or to subtract the value in one column from the value in another (e.g. After - Before.

To use the value of a column on the current row in a Filter or Calculate, just type the name of the column, e.g. Price. If you need to use the whole column as a list, use the table name and column name like this: table\\Sales#Price. The entire column can be useful if you need access to specific entries using the element(..) or element or(..) functions, but an Aggregate transformation which helps to calculate column totals, averages, etc, may be more appropriate.

Numeric calculations

The standard add (+), subtract (-), multiply (*), divide (/) and raise-to-power (^) operators can be used to perform calculations. If the order of operations is unclear, the operators must be bracketed, e.g. 1 + 2 * 3 is not allowed, it must either be written (1 + 2) * 3 or 1 + (2 * 3).

A useful feature to avoid mistakes is the idea of units of measure. Numbers can be given units, and they will be tracked through calculations, converted between, and checked. For example, 15{m} means 15 metres, and 15{m} / 60{s} will give 0.25{m/s}. If you try to compare mismatching units such as (15{m} / 60{s}) < 10{mile/hour} then you will get an error, which helps avoid mistakes with different quantities. More information on this is available in the units guide.

Text manipulation

Text values can be joined together using the ; operator. For example, "a" ; "bc" ; "d" gives "abcd". Lists of text can be joined together with join text(..) or if a separator (e.g. a comma) is needed between each item then join text with(..).

Text is stored as Unicode, meaning that it can deal with non-English characters, emoji and the rest of the Unicode standard.

Comparison

You can use the less-than (<), less-than-or-equal-to (<=), greater-than (>) and greater-than-or-equal-to(>=) operators to compare values. You can also chain together several operators of the same direction, for example 1 <= row <= 10 checks if row is between 1 and 10 (inclusive).

If you need the lowest or highest item in a list, you can use the minimum(..) and maximum(..), which work on any types (numbers, text, dates, etc).

Equality

You can use the equals (=) and not equals (<>) operators to compare values for being the same. You can only compare items which have the same type.

It is also possible to compare a value against a pattern such as 5 ± 0.01 (meaning within 0.01 of 5) or "The" ; _ (meaning any text beginning with "The"). See the section on patterns for more details.

Patterns

A pattern is a way to flexibly compare against a value. For example, you may want to check if two columns have almost the same numeric value, Actual = Forecast will be false if say Actual was 2.356 and Forecast was 2.353. Instead you can use Actual = Forecast ± 0.01 which checks if they are within 0.01 of each other.

Another example where patterns are very useful is in text matching. Often you want to match the beginning, something in the middle, or the end, but not the entire text. Animal = _ ; "cat" will check if the Animal text ends with "cat". Underscore means match anything, as you want to match anything followed by cat. If you want to use the value of part of the pattern afterwards then you can put a name with the underscore, for example @if Animal = _type ; "cat" @then "Cat type: " ; type @else "Not a cat" @endif applied to "Wildcat" will give back "Cat type: Wild".

If Then Else

It is very common to need to choose between two alternatives based on some condition. For example, @if Amount >= 0 @then "Incoming" @else "Outgoing" @endif chooses which text to display based on the value in the amount column.

The format is always @if condition @then value_if_true @else value_if_false @endif. The condition part usually involves a comparison or an equality check, the latter of which may feature a pattern.

If you need to compare one value against many possibilities, you may find a match expression better than multiple if-then-else expressions.

Match Expressions

Sometimes you may want to compare a value against multiple possible alternatives. For example, @match Level @case "High" @then 2 @case "Medium" @then 1 @case "Low" @then 0 @endmatch matches Level against High/Medium/Low and gives back the number 2/1/0 respectively.

It is also possible to attach extra conditions to each case using @given, for example @match Amount @case n @given n < 0 @then "Negative" @case 0 @then "Zero" @case n @given n > 0 @then "Positive" @endmatch works out whether the number is negative, zero or positive.

Cases inside a match are checked in order, so often the last item can be @case _ meaning match anything, given that that the earlier matches failed. If no cases match then an error will occur.

Converting between types

Projects often have a mix of different types, and it is common to require conversion between different types. Which conversion is required depends on which types are involved.

Converting from text to another type

Converting from text is primarily done using the from text(..) and from text to(..) functions. The destination type that is being converted from needs to be known ahead of time. Often this is apparent from the context and thus the simpler from text(..) can be used. In cases where this is not possible, you will be prompted to use from text to(..) instead, where the type is specified.

Converts to number@call function\\from text("63.3") + 164.3@if @call function\\from text("true") @then 1 @else 0 @endif1Tries converting to number, but text is not a valid number@call function\\from text("true") + 1errorConverts to a time@call function\\from text to(type{Time}, "7:35PM")time{19:35}Converts to list of numbers (note the square brackets)@call function\\from text to(type{[Number]}, "[1, 2]")[1, 2]Converting from another type to text

Converting to text is primarily done using the to text(..) function. This converts each type into its standard text form. Several examples are below. Most types are straightforward, although note that times convert to the 24-hour clock and dates convert to the ISO 8601 YYYY-MM-DD format.

@call function\\to text(10 - 45.60)"-35.6"@call function\\to text([true, "a" = "b"])"[true, false]"@call function\\to text(datetime{14 April 2008 5:38PM})"2018-04-14 17:38"Converting numbers between different units

There are three main types of conversion (with more examples below):

@call function\\convert unit({mile/hour}, 10{m/s})22.369 ± 0.001@call function\\convert unit({m/s}, 1{mile} / 4{minute})6.705 ± 0.0015262.3 * 1{kg}5262.3{kg}32.5{year} / 1{year}32.5Converting between different date and time types

Converting between different date and time types is usually a matter of composing, decomposing or differencing.

Composing means making smaller parts into larger, for example converting a Date and a Time into a DateTime, or converting a year and a month into a DateYM. Such functions are:

Decomposing larger parts into smaller items, for example extracting the Date from a DateTime, or the year from a Date. Such functions are:

Differencing means comparing two items of the same date or time type, to work out the distance between them. Such functions are:

@call function\\dateym from ym(2008{year}, 3{month})dateym{2008-03}Units of measure

Numbers often relate to some unit, such as metres, dollars, grams, items sold, and so on. Units of measure allow you to track which units a number is measured in, which can help you to remember the units, and also check that two numbers with different units are not compared (e.g. comparing centimetres to metres, or dollars to euros).

How to start using units

Units are written after the Number type in curly brackets. How to add units depends on the context:

Several units are built-in to the software, such as m (metres), s (seconds), year, USD (US dollars) and more.

More details

Units can be multiplied together or divided, for example speed might use the unit {m/s} (metres per second) and acceleration might be {m/s^2}. These units are tracked when you multiply or divide, so 10{m} / 4{s} gives 2.5{m/s}, and 30{USD}/2{hour} gives 15{USD/hour}.

Many numeric functions work on numbers with units, and preserve them, e.g. abs(-1{hour}) will give back 1{hour}.

For information on converting between units, see the conversion guide.

Optional Type

Sometimes you have data where a value can be blank or missing. For example, you may have a column for when a product stopped being sold, but many of the products are still on sale. If you make the column have type Date, you will be required to give a value. But if you make the column Optional(Date) then you can have blank values.

A value for an optional type is always either:

The present items have to be wrapped with Is(...), even though this may seem cumbersome at times.

How to extract values from Optional

The most common case is to supply a default value for a missing item. This can be done with the "get optional or" function, for example get optional or(Your optional column, 0) will get the number out of a column named "Your optional column" or zero if the column has a blank value.

You can also use pattern matches. The most common form is: @if Optional Column =~ Is(x) @then x @else default value @endif The =~ operator means that the right hand side is a pattern, so x takes on the value if present, and the then-part is evaluated. Otherwise the else-part is evaluated and you must supply the missing value.

If you are certain the value will not be missing then you can write get optional(Your optional column) without specifying a default; an error will occur if the value is blank.

How to convert values to Optional

If you want to convert a value to Optional, you write Is() around it. For example, Is(Price) takes the value of a number column Price and converts it to Optional(Number).

It is quite common to want to convert values to optional where missing has been encoded as a dummy value. For example, you may have a Height column where -1 has been used to indicate missing. In that case, the expression @if Height = -1 @then None @else Is(Height) @endif will convert -1 to blank, but all other values to present.