List

The List type has the following methods.

contains

List.contains: (self: List[T], element: T) -> Bool

Return whether the list contains a given element. For example:

[for needle in ["a", "z"]: ["a", "b", "c"].contains(needle)]
// Evaluates to:
[true, false]

enumerate

List.enumerate: (self: List[T]) -> Dict[Int, T]

Return a mapping from index to list element. The index is zero-based.

["x", "y", "z"].enumerate()
// Evaluates to:
{ 0: "x", 1: "y", 2: "z" }

This is mostly useful for iterating:

let pieces = ["pawn", "queen", "bisshop"];
let unordered_pairs = [
  for i, piece_i in pieces.enumerate():
  for j in std.range(i + 1, pieces.len()):
  let piece_j = pieces[j];
  [piece_i, piece_j]
];
unordered_pairs
// Evaluates to:
[["pawn", "queen"], ["pawn", "bisshop"], ["queen", "bisshop"]]

filter

List.filter: (self: List[T], predicate: T -> Bool) -> List[T]

Construct a new list that contains only the elements where predicate returned true. The result is equivalent to a list comprehension, a and b are identical in this example:

let xs = [1, 2, 3];
let a = xs.filter(x => x > 1);
let b = [
  for x in xs:
  if x > 1:
  x
];
// Both a and b evaluate to:
[2, 3]

List comprehensions are more general than filter: they support nested loops, and let-bindings are accessible to the inner scope. Still, filter can be useful, especially for iteratively refining a query in an rcl query command.

flat_map

List.flat_map: (self: List[T], map_element: T -> List[U]) -> List[U]

Construct a new list by taking every element in the list, applying map_element to it (which should return a collection), and concatenating those results. flat_map is like map, except that it flattens the result. It is equivalent to a list comprehension with a nested loop: a and b are identical in this example:

let apps = [
  { name = "sshd", ports = [22] },
  { name = "nginx", ports = [80, 443] },
];
let a = apps.flat_map(app => app.ports);
let b = [
  for app in apps:
  for port in app.ports:
  port
];
// Both a and b evaluate to:
[22, 80, 443]

List comprehensions are often clearer in configuration files, especially when the body is large. They are also more general: list comprehensions support arbitrary nesting, filtering with if, and let-bindings are accessible to the inner scope. Still, flat_map can be useful, especially for iteratively refining a query in an rcl query command.

fold

List.fold: (self: List[T], seed: U, reduce: (U, T) -> U) -> U

Left-fold the function reduce over the list, with seed as the initial accumulator value.

[2, 3, 5, 7, 11].fold(
  { min = 99, max = 0 },
  (acc, x) => {
    min = if acc.min < x: acc.min else: x,
    max = if acc.max > x: acc.max else: x,
  },
)
// Evaluates to:
{ max = 11, min = 2 }

group_by

List.group_by: (self: List[T], get_key: T -> U) -> Dict[U, List[T]]

Group the elements of the list by a key selected by get_key. Within groups, the original order of elements is preserved.

let foods = [
  { category = "fruit", name = "apple" },
  { category = "fruit", name = "pear" },
  { category = "vegetable", name = "onion" },
  { category = "vegetable", name = "carrot" },
];
foods.group_by(food => food.category)

// Evaluates to:
{
  fruit = [
    { category = "fruit", name = "apple" },
    { category = "fruit", name = "pear" },
  ],
  vegetable = [
    { category = "vegetable", name = "onion" },
    { category = "vegetable", name = "carrot" },
  ],
}

join

List.join: (self: List[T], separator: String) -> String

Concatenate the elements with the separator in between. This is equivalent to using a format string, therefore the list elements must be string formattable.

// Evaluates to "foo-bar".
["foo", "bar"].join("-")

// Evaluates to "2,3,5".
[2, 3, 5].join(",")

// Error, dicts are not string formattable.
[{}, {}].join("")

key_by

List.key_by: (self: List[T], get_key: T -> U) -> Dict[U, T]

Build a dictionary with the key selected by get_key as key, and the list elements as values. The keys must be unique. When a key is not unique, this method fails and reports the conflicting values.

let replicants = [
  { name = "rachael", generation = 7 },
  { name = "rbatty", generation = 6 },
  { name = "zsalome", generation = 6 },
];

replicants.key_by(r => r.name)
// Evaluates to:
{
  rachael = { name = "rachael", generation = 7 },
  rbatty = { name = "rbatty", generation = 6 },
  zsalome = { name = "zsalome", generation = 6 },
}

replicants.key_by(r => r.generation)
// This fails with the following error:
// Error: The key 6 is not unique. The following values use this key:
//   { generation = 6, name = "rbatty" }
//   { generation = 6, name = "zsalome" }

len

List.len: (self: List[T]) -> Int

Return the number of elements in the list. For example:

// Evaluates to 3.
[1, 2, 3].len()

map

List.map: (self: List[T], map_element: T -> U) -> List[U]

Construct a new list by applying map_element to every element in the list. The result is equivalent to a list comprehension, a and b are identical in this example:

let xs = [1, 2, 3];
let a = [for x in xs: x * 2];
let b = xs.map(x => x * 2);
// Both a and b evaluate to:
[2, 4, 6]

List comprehensions are often clearer in configuration files, especially when the body is large. They are also more general: list comprehensions support nested loops and filtering with if. Still, map can be useful, especially for iteratively refining a query in an rcl query command.

reverse

List.reverse: (self: List[T]) -> List[T]

Return the list in reverse.

[1, 2, 3].reverse()
// Evaluates to:
[3, 2, 1]

sum

List.sum: (self: List[Int]) -> Int

Return the sum of the elements in the list. For example:

// Evaluates to 42.
[3, 7, 11, 21].sum()