Skip to content

Comprehensions

Comprehensions provide a convenient syntax for constructing a new table by iterating over some existing object and applying an expression to its values. There are two kinds of comprehensions: list comprehensions and table comprehensions. They both produce Lua tables; list comprehensions accumulate values into an array-like table, and table comprehensions let you set both the key and the value on each iteration.

List Comprehensions

The following creates a copy of the items table but with all the values doubled.

yuescript
items = [ 1, 2, 3, 4 ]
doubled = [item * 2 for i, item in ipairs items]
yue
items = [ 1, 2, 3, 4 ]
doubled = [item * 2 for i, item in ipairs items]

The items included in the new table can be restricted with a when clause:

yuescript
slice = [item for i, item in ipairs items when i > 1 and i < 3]
yue
slice = [item for i, item in ipairs items when i > 1 and i < 3]

Because it is common to iterate over the values of a numerically indexed table, an * operator is introduced. The doubled example can be rewritten as:

yuescript
doubled = [item * 2 for item in *items]
yue
doubled = [item * 2 for item in *items]

In list comprehensions, you can also use the spread operator ... to flatten nested lists, achieving a flat map effect:

yuescript
data =
  a: [1, 2, 3]
  b: [4, 5, 6]

flat = [...v for k,v in pairs data]
-- flat is now [1, 2, 3, 4, 5, 6]
yue
data =
  a: [1, 2, 3]
  b: [4, 5, 6]

flat = [...v for k,v in pairs data]
-- flat is now [1, 2, 3, 4, 5, 6]

The for and when clauses can be chained as much as desired. The only requirement is that a comprehension has at least one for clause.

Using multiple for clauses is the same as using nested loops:

yuescript
x_coords = [4, 5, 6, 7]
y_coords = [9, 2, 3]

points = [ [x, y] for x in *x_coords \
for y in *y_coords]
yue
x_coords = [4, 5, 6, 7]
y_coords = [9, 2, 3]

points = [ [x, y] for x in *x_coords \
for y in *y_coords]

Numeric for loops can also be used in comprehensions:

yuescript
evens = [i for i = 1, 100 when i % 2 == 0]
yue
evens = [i for i = 1, 100 when i % 2 == 0]

Table Comprehensions

The syntax for table comprehensions is very similar, only differing by using { and } and taking two values from each iteration.

This example makes a copy of the tablething:

yuescript
thing = {
  color: "red"
  name: "fast"
  width: 123
}

thing_copy = {k, v for k, v in pairs thing}
yue
thing = {
  color: "red"
  name: "fast"
  width: 123
}

thing_copy = {k, v for k, v in pairs thing}
yuescript
no_color = {k, v for k, v in pairs thing when k != "color"}
yue
no_color = {k, v for k, v in pairs thing when k != "color"}

The * operator is also supported. Here we create a square root look up table for a few numbers.

yuescript
numbers = [1, 2, 3, 4]
sqrts = {i, math.sqrt i for i in *numbers}
yue
numbers = [1, 2, 3, 4]
sqrts = {i, math.sqrt i for i in *numbers}

The key-value tuple in a table comprehension can also come from a single expression, in which case the expression should return two values. The first is used as the key and the second is used as the value:

In this example we convert an array of pairs to a table where the first item in the pair is the key and the second is the value.

yuescript
tuples = [ ["hello", "world"], ["foo", "bar"]]
tbl = {unpack tuple for tuple in *tuples}
yue
tuples = [ ["hello", "world"], ["foo", "bar"]]
tbl = {unpack tuple for tuple in *tuples}

Slicing

A special syntax is provided to restrict the items that are iterated over when using the * operator. This is equivalent to setting the iteration bounds and a step size in a for loop.

Here we can set the minimum and maximum bounds, taking all items with indexes between 1 and 5 inclusive:

yuescript
slice = [item for item in *items[1, 5]]
yue
slice = [item for item in *items[1, 5]]

Any of the slice arguments can be left off to use a sensible default. In this example, if the max index is left off it defaults to the length of the table. This will take everything but the first element:

yuescript
slice = [item for item in *items[2,]]
yue
slice = [item for item in *items[2,]]

If the minimum bound is left out, it defaults to 1. Here we only provide a step size and leave the other bounds blank. This takes all odd indexed items: (1, 3, 5, …)

yuescript
slice = [item for item in *items[,,2]]
yue
slice = [item for item in *items[,,2]]

Both the minimum and maximum bounds can be negative, which means that the bounds are counted from the end of the table.

yuescript
-- take the last 4 items
slice = [item for item in *items[-4,-1]]
yue
-- take the last 4 items
slice = [item for item in *items[-4,-1]]

The step size can also be negative, which means that the items are taken in reverse order.

yuescript
reverse_slice = [item for item in *items[-1,1,-1]]
yue
reverse_slice = [item for item in *items[-1,1,-1]]

Slicing Expression

Slicing can also be used as an expression. This is useful for getting a sub-list of a table.

yuescript
-- take the 2nd and 4th items as a new list
sub_list = items[2, 4]

-- take the last 4 items
last_four_items = items[-4, -1]
yue
-- take the 2nd and 4th items as a new list
sub_list = items[2, 4]

-- take the last 4 items
last_four_items = items[-4, -1]