ESC
Type to search...
S
Soli Docs

Arrays

Ordered, mutable collections with powerful built-in methods for transformation and manipulation.

Creating Arrays

Array Literals

Create arrays using square brackets with comma-separated values.

# Basic array literal
numbers = [1, 2, 3, 4, 5]
names = ["Alice", "Bob", "Charlie"]
mixed = [1, "hello", true, null]

# Empty array
empty = []

# Nested arrays
matrix = [[1, 2], [3, 4], [5, 6]];
Type Annotations

Optionally specify array types for clarity and type safety.

# Typed arrays
let scores: Int[] = [95, 87, 92, 88, 90]
let names: String[] = ["Alice", "Bob", "Charlie"]
let flags: Bool[] = [true, false, true]

# Multi-dimensional arrays
let matrix: Int[][] = [[1, 2], [3, 4], [5, 6]];
range(start, end, step?)

Creates an array of numbers from start to end (exclusive).

range(0, 5)        # [0, 1, 2, 3, 4]
range(1, 10)       # [1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 10, 2)    # [0, 2, 4, 6, 8] (step of 2)
range(5, 0, -1)    # [5, 4, 3, 2, 1] (reverse)
%w[] / %i[] / %n[]

Shorthand syntax for creating arrays of strings (%w), symbols (%i), or numbers (%n). Elements are separated by whitespace. Use D suffix for decimals.

# %w[] creates string arrays
words = %w[foo bar baz]
print(words);  # ["foo", "bar", "baz"]

# %i[] creates symbol arrays
methods = %i[get post put delete]
print(methods);  # [:get, :post, :put, :delete]

# %n[] creates number arrays (integers, floats, and decimals with D suffix)
nums = %n[1 2.5 3.5D]
print(nums);  # [1, 2.5, 3.5]

# Empty arrays
%w[]  # []
%i[]  # []
%n[]  # []

# Multiline for readability
const HTTP_METHODS = %i[
  get
  post
  put
  delete
  patch
]

# Number arrays for coordinates
const BOX = %n[0 0 100 100]

# Equivalent to regular arrays
%w[a b c] == ["a", "b", "c"]
%i[a b c] == [:a, :b, :c]
%n[1 2 3] == [1, 2, 3]
%n[1.5D 2.5D] == [1.5D, 2.5D]

Accessing Elements

Index Access

Access elements using zero-based indexing. Negative indices count from the end.

arr = [10, 20, 30, 40, 50]

# Zero-based indexing
print(arr[0]);    # 10 (first element)
print(arr[2]);    # 30 (third element)

# Negative indexing
print(arr[-1]);   # 50 (last element)
print(arr[-2]);   # 40 (second to last)

# Out of bounds returns null
print(arr[10]);   # null
Modifying Elements

Arrays are mutable - you can modify elements by index.

arr = [1, 2, 3, 4, 5]

# Modify existing element
arr[0] = 100
print(arr);  # [100, 2, 3, 4, 5]

# Add new element (extends array)
arr[10] = 999
print(arr);  # [100, 2, 3, 4, 5, null, null, null, null, null, 999]

Array Operators

-

Subtract an array from another array, removing all matching elements. Returns a new array (original unchanged). Uses identity comparison for instances.

# Basic array subtraction
a = [1, 2, 3]
b = [1]
result = a - b
print(result);  # [2, 3]

# Remove multiple occurrences
a = [1, 2, 1, 3, 1]
b = [1]
result = a - b
print(result);  # [2, 3]

# Works with strings
fruits = ["apple", "banana", "cherry"]
to_remove = ["banana"]
print(fruits - to_remove);  # ["apple", "cherry"]

# Instance comparison uses identity (pointer equality)
class Person {
  name: String;
  fn new(n) { this.name = n; }
}
p1 = Person.new({"name": "Alice"})
p2 = Person.new({"name": "Bob"})
arr = [p1, p2]
result = arr - [p1]
print(result);  # []
+

Concatenate two arrays, returning a new array containing all elements from both. Original arrays are unchanged.

# Concatenate two arrays
a = [1, 2]
b = [3, 4]
result = a + b
print(result);  # [1, 2, 3, 4]

# Works with any types
nums = [1, 2] + [3, 4]
strings = ["a", "b"] + ["c", "d"]

# Original arrays unchanged
x = [1, 2]
y = [3, 4]
combined = x + y
print(x);  # [1, 2]
print(y);  # [3, 4]

Array Methods

.map(def)

Transform each element using a function. Returns a new array.

numbers = [1, 2, 3, 4, 5]

# Double each number
doubled = numbers.map(|x| x * 2)
print(doubled);  # [2, 4, 6, 8, 10]

# Trailing block syntax (equivalent)
doubled = numbers.map |x| x * 2 end

# Symbol shorthand: &:method → |__it| __it.method()
strings = numbers.map(&:to_s)
print(strings);  # ["1", "2", "3", "4", "5"]

# Convert to strings
strings = numbers.map(|x| str(x))
print(strings);  # ["1", "2", "3", "4", "5"]
.filter(def)

Keep elements matching a condition. Returns a new array.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Keep only even numbers
evens = numbers.filter(|x| x % 2 == 0)
print(evens);  # [2, 4, 6, 8, 10]

# Trailing block syntax (equivalent)
evens = numbers.filter |x| x % 2 == 0 end

# Symbol shorthand: &:predicate → |__it| __it.predicate()
evens = numbers.filter(&:even?)
print(evens);  # [2, 4, 6, 8, 10]

# Keep numbers greater than 5
large = numbers.filter(|x| x > 5)
print(large);  # [6, 7, 8, 9, 10]
.reduce(def, initial)

Accumulate array elements to a single value.

numbers = [1, 2, 3, 4, 5]

# Sum all numbers
sum = numbers.reduce(|acc, x| acc + x, 0)
print(sum);  # 15

# Trailing block syntax — extra args in parens, block after
sum = numbers.reduce(0) |acc, x| acc + x end

# Product of all numbers
product = numbers.reduce(|acc, x| acc * x, 1)
print(product);  # 120
.each(def)

Iterate over elements for side effects. Returns the original array.

numbers = [1, 2, 3]

# Print each number
numbers.each(|x| print(x))
# Output: 1
#         2
#         3

# Trailing block syntax (equivalent)
numbers.each |x| print(x) end

# Modify external state
sum = 0;
numbers.each |x| sum = sum + x end
print(sum);  # 6
.each_with_index(def)

Iterate over elements with their index. The block receives (value, index). Returns the original array.

names = ["Alice", "Bob", "Charlie"]

names.each_with_index(|name, i| print("\(i): \(name)"))
# Output: 0: Alice
#         1: Bob
#         2: Charlie

# Trailing block syntax (equivalent)
names.each_with_index |name, i| print("\(i): \(name)") end
.find(def) / .any?(def) / .all?(def)

Search and check array elements.

numbers = [1, 2, 3, 4, 5]

# find - returns first matching element
first_even = numbers.find(|x| x % 2 == 0)
print(first_even)  # 2

# any? - returns true if any element matches
has_large = numbers.any?(|x| x > 4)
print(has_large)  # true

# all? - returns true if all elements match
all_positive = numbers.all?(|x| x > 0)
print(all_positive)  # true

# Trailing block syntax works too
found = numbers.find |x| x > 3 end
any = numbers.any? |x| x > 4 end
all = numbers.all? |x| x > 0 end
.index_of(value)

Return the position of the first element equal to value, or -1 if not found.

names = ["Alice", "Bob", "Charlie", "Bob"]

print(names.index_of("Bob"));      # 1  (first match)
print(names.index_of("Charlie"));  # 2
print(names.index_of("Dave"));     # -1 (not found)
.sort / .sort(def)

Sort elements. Default is ascending order, or provide a custom comparator.

numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# Default sort (ascending)
sorted = numbers.sort
print(sorted);  # [1, 1, 2, 3, 4, 5, 6, 9]

# Custom comparator (descending)
desc = numbers.sort(|a, b| b - a)
print(desc);  # [9, 6, 5, 4, 3, 2, 1, 1]

# Trailing block syntax
desc = numbers.sort |a, b| b - a end

# Sort by string length
words = ["cherry", "pie", "apple"]
by_length = words.sort |a, b| len(a) - len(b) end
print(by_length);  # ["pie", "apple", "cherry"]
.sort_by(key) / .sort_by(def)

Sort an array of hashes by a key, or use a function to extract the sort value from each element.

people = [
 { "name": "Charlie", "age": 30 },
 { "name": "Alice", "age": 25 },
 { "name": "Bob", "age": 20 }
]

# Sort by hash key (string argument)
by_name = people.sort_by("name")
print(by_name.map(|p| p.get("name")));  # [Alice, Bob, Charlie]

by_age = people.sort_by("age")
print(by_age.map(|p| p.get("age")));  # [20, 25, 30]

# Sort by function (for computed values)
words = ["cherry", "pie", "apple"]
by_length = words.sort_by(|w| len(w))
print(by_length);  # [pie, apple, cherry]
.reverse

Return a new array with elements in reverse order.

arr = [1, 2, 3, 4, 5]
reversed = arr.reverse
print(reversed);  # [5, 4, 3, 2, 1]
print(arr);       # [1, 2, 3, 4, 5] (original unchanged)
.uniq

Remove duplicate elements. Returns a new array with unique values.

arr = [1, 2, 2, 3, 3, 3, 4]
unique = arr.uniq
print(unique)  # [1, 2, 3, 4]
.compact

Remove null values from the array.

arr = [1, null, 2, null, 3, null]
cleaned = arr.compact
print(cleaned)  # [1, 2, 3]
.compact_blank

Remove null, empty strings, empty arrays, and empty hashes from the array.

arr = [1, null, "", [], {}, "hello", [1], {a: 1}]
cleaned = arr.compact_blank
print(cleaned)  # [1, "hello", [1], {a: 1}]
.flatten / .flatten(n)

Flatten nested arrays. Optionally specify depth.

nested = [[1, 2], [3, 4], [5, 6]]
flat = nested.flatten
print(flat);  # [1, 2, 3, 4, 5, 6]

# Flatten only 1 level deep
deep = [[1, 2], [[3, 4]]]
partial = deep.flatten(1)
print(partial);  # [1, 2, [3, 4]]
.first / .last

Get the first or last element.

arr = [10, 20, 30, 40, 50]
print(arr.first);  # 10
print(arr.last);   # 50

# Returns null for empty arrays
empty = []
print(empty.first);  # null
print(empty.last);   # null
.empty? / .blank? / .present? / .includes?(value) / .contains(value)

Check if array is empty, blank, present, or contains a value. .includes? and .contains are aliases.

arr = [1, 2, 3]

print(arr.empty?);         # false
print(arr.blank?);         # false
print(arr.present?);       # true
print(arr.includes?(2));   # true
print(arr.contains(2));    # true

empty = []
print(empty.empty?);       # true
print(empty.blank?);       # true
print(empty.present?);     # false
.sample / .shuffle

Random element selection and shuffling.

arr = [1, 2, 3, 4, 5]

# Get random element
random = arr.sample
print(random);  # Random element (e.g., 3)

# Shuffle array
shuffled = arr.shuffle
print(shuffled);  # Randomized order (e.g., [3, 1, 5, 2, 4])
.take(n) / .drop(n)

Take or skip the first n elements.

arr = [1, 2, 3, 4, 5]

# Take first n elements
first_three = arr.take(3)
print(first_three);  # [1, 2, 3]

# Skip first n elements
rest = arr.drop(3)
print(rest);  # [4, 5]
.slice(start?, end?)

Extract a sub-array by start and end index. Both arguments are optional and support negative indices counting from the end. Returns a new array (non-mutating).

arr = [1, 2, 3, 4, 5]

# Extract by start and end index
print(arr.slice(1, 3));  # [2, 3]

# Negative start (last n elements)
print(arr.slice(-2));     # [4, 5]

# Negative end (all but last n)
print(arr.slice(1, -1));  # [2, 3, 4]

# Out of bounds returns empty
print(arr.slice(5));     # []

# No args returns a copy
copy = arr.slice()
print(copy);             # [1, 2, 3, 4, 5]

# Original unchanged
print(arr);              # [1, 2, 3, 4, 5]
.zip(other)

Combine two arrays into pairs.

names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]

pairs = names.zip(scores)
# [["Alice", 95], ["Bob", 87], ["Charlie", 92]]
.sum / .min / .max

Numeric aggregation methods.

numbers = [10, 5, 8, 3, 12]

print(numbers.sum);   # 38.0
print(numbers.min);   # 3
print(numbers.max);   # 12
.join(delimiter)

Joins array elements into a string using the specified delimiter.

words = ["Hello", "World"]
print(words.join(" "));   # "Hello World"

numbers = [1, 2, 3, 4, 5]
print(numbers.join("-")); # "1-2-3-4-5"

paths = ["usr", "local", "bin"]
print(paths.join("/"));   # "usr/local/bin"

Method Chaining

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Chain multiple methods together
result = numbers
  .filter(|x| x % 2 == 0)   # Keep evens: [2, 4, 6, 8, 10]
  .map(|x| x * x)           # Square them: [4, 16, 36, 64, 100]
  .filter(|x| x < 50)       # Keep less than 50: [4, 16, 36]

print(result);  # [4, 16, 36]

# With pipeline operator
result2 = numbers
  |> filter(|x| x > 5)
  |> map(|x| x * 2)
  |> sum
print(result2);  # 90

Array Class Methods

Array literals are automatically wrapped in an Array class instance that provides methods for manipulation and transformation. Each array value has access to these methods via dot notation.

to_string()

Returns a formatted string representation of the array. Called automatically in REPL.

arr = [1, 2, 3]
# In REPL, displays: [1, 2, 3]
# Equivalent to: arr.to_string
to_json

Serializes the array to a JSON string.

arr = [1, "hello", true, null]
arr.to_json  # '[1,"hello",true,null]'

nested = [{name: "Alice"}, {name: "Bob"}]
nested.to_json  # '[{"name":"Alice"},{"name":"Bob"}]'
.length / .len / .size

Returns the number of elements in the array.

arr = [1, 2, 3]
arr.length  # 3
arr.len     # 3
arr.size    # 3
push(value) / pop()

Adds an element to the end or removes and returns the last element.

arr = []
arr.push(1)  # [1]
arr.push(2)  # [1, 2]
last = arr.pop  # 2, arr is now [1]
get(index)

Returns the element at the specified index. Supports negative indices for backward access.

arr = ["first", "second", "third"]
arr.get(0);      # "first"
arr.get(2);      # "third"
arr.get(-1);     # "third" (last)
arr.get(-2);     # "second"
clear()

Removes all elements from the array.

arr = [1, 2, 3]
arr.clear
arr.length  # 0
.delete(value) / .delete_at(index)

delete removes all elements equal to value — returns the mutated array or null if not found. delete_at removes the element at the given index (supports negative indices).

[1, 2, 3, 2].delete(2)   # [1, 3]
[1, 2].delete(99)          # null
[1, 2, 3].delete_at(1)     # [1, 3]
[1, 2, 3].delete_at(-1)    # [1, 2]
.shift / .unshift(value)

shift removes the first element. unshift prepends elements to the front. Both return a new array (strings are immutable).

[1, 2, 3].shift       # [2, 3]
[].shift               # null
[1, 2].unshift(0)      # [0, 1, 2]
.insert(index, value, ...)

Inserts values at the given index. Supports negative indices. Multiple values can be inserted at once.

[1, 3].insert(1, 2)       # [1, 2, 3]
[1, 4].insert(1, 2, 3)     # [1, 2, 3, 4]
.rotate(count?)

Rotates the array by count positions (default 1). Positive moves elements from left to right; negative goes the other way.

[1, 2, 3].rotate       # [2, 3, 1]
[1, 2, 3].rotate(2)     # [3, 1, 2]
[1, 2, 3].rotate(-1)    # [3, 1, 2]
.reject(def) / .none?(def) / .one?(def)

reject is the inverse of filter — keeps items where the block returns false. none? returns true if no items match. one? returns true if exactly one matches.

[1, 2, 3, 4].reject(fn(x) x % 2 == 0)   # [1, 3]
[1, 2].none?(fn(x) x > 10)                # true
[1, 2, 3].one?(fn(x) x == 2)              # true
[1, 2, 2].one?(fn(x) x == 2)              # false
.values_at(index, ...)

Returns an array containing values at the specified indices. Supports negative indices and arrays of indices.

[10, 20, 30, 40].values_at(0, 2)    # [10, 30]
[10, 20, 30].values_at(0, -1)        # [10, 30]
.count(value?)

With no argument returns the length. With an argument counts occurrences. With a function counts elements where the block returns truthy.

[1, 2, 3].count        # 3
[1, 2, 2, 3].count(2)   # 2
[1, 2, 3, 4].count(fn(x) x % 2 == 0)   # 2
.class / .inspect / .nil? / .is_a?

Type introspection methods available on all types.

arr = [1, 2, 3]
arr.class         # "array"
arr.inspect       # "[1, 2, 3]"
arr.nil?          # false
arr.is_a?("array")  # true