List

The List type has the following methods.

all

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

Return whether the predicate is true for all elements in the list. For example:

// Evaluates to true.
[11, 17, 42].all(x => x > 0)

// Evaluates to false.
[11, 17, 42].all(x => x > 20)

// Evaluates to true.
[].all(x => false)

This method short-circuits: when the outcome is decided, it will not call the predicate for the remaining elements in the list.

any

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

Return whether the predicate is true for any element in the list. For example:

// Evaluates to true.
[11, 17, 42].any(x => x > 17)

// Evaluates to false.
[11, 17, 42].any(x => x > 42)

// Evaluates to false.
[].any(x => true)

This method short-circuits: when the outcome is decided, it will not call the predicate for the remaining elements in the list.

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]

sort

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

Return a sorted version of the list. Elements of the same type will be sorted with respect to each other. The relative order of elements of different types is an implementation detail that may change between versions.

[11, 5, 7].sort()
// Evaluates to:
[5, 7, 11]

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()