Arrays
Arrays are ordered, mixed-type collections and the workhorse of data processing in Parsley. They support indexing, slicing, functional operations, set algebra, random sampling, locale-aware formatting, and more.
[1, "hello", true, null, £5.00] // mixed types, including money
[[1, 2], [3, 4]] // nested arrays
[] // empty (falsy)
Operators
Indexing & Slicing
let cities = ["London", "Paris", "Tokyo"]
cities[0] // "London"
cities[-1] // "Tokyo" (negative = from end)
cities[?10] // null (optional access, no error)
let n = [10, 20, 30, 40, 50]
n[1:3] // [20, 30] (start inclusive, end exclusive)
n[:2] // [10, 20]
n[2:] // [30, 40, 50]
⚠️ Optional indexing
[?n]returnsnullinstead of an error on out-of-bounds access. Most languages don't have this.
An index or slice bracket must open on the same line as the expression it indexes. A [ at the start of a new line always begins a new array literal, never an index into the previous line's value:
let xs = [10, 20, 30]
xs[0] // 10 — index (same line)
let ys = [10, 20, 30]
[0] // [0] — a new array literal, not ys[0]
Concatenation & Repetition
[1, 2] ++ [3, 4] // [1, 2, 3, 4]
[1, 2] * 3 // [1, 2, 1, 2, 1, 2]
⚠️
++concatenates arrays,+does not."a" ++ "b"produces["a", "b"]— the++operator always creates/extends arrays. Use+for string concatenation.
Chunking with /
The division operator chunks an array into groups — unique to Parsley:
[1, 2, 3, 4, 5, 6, 7, 8, 9] / 3 // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[1, 2, 3, 4, 5] / 2 // [[1, 2], [3, 4], [5]]
This is useful for pagination, grid layouts, and batch processing.
Set Operations
Logical operators become set operations on arrays:
[1, 2, 3] && [2, 3, 4] // [2, 3] (intersection)
[1, 2] || [2, 3] // [1, 2, 3] (union)
[1, 2, 3] - [2] // [1, 3] (subtraction)
Membership
2 in [1, 2, 3] // true
5 not in [1, 2, 3] // true
"admin" in null // false (null-safe, no error)
Ranges
The .. operator creates arrays of sequential integers:
1..5 // [1, 2, 3, 4, 5]
for Loops Return Arrays
This is one of Parsley's most distinctive features: for is an expression that returns an array, making it a built-in map and filter:
let doubled = for (n in [1, 2, 3]) { n * 2 } // [2, 4, 6]
let evens = for (n in 1..10) { if (n % 2 == 0) { n } } // [2, 4, 6, 8, 10]
// With index
let labeled = for (i, city in ["London", "Paris"]) {
`{i + 1}. {city}`
}
// ["1. London", "2. Paris"]
⚠️ If the body returns
null(e.g. anifwith noelse), that element is omitted from the result — this is how filtering works. Usestopandskipinstead ofbreakandcontinue.
Throughout the methods below, many examples show both .method(fn) and for style — use whichever reads better for your case.
Methods
filter
[1, 2, 3, 4, 5].filter(fn(x) { x > 2 }) // [3, 4, 5]
// Equivalent with for:
for (x in [1, 2, 3, 4, 5]) { if (x > 2) { x } } // [3, 4, 5]
fmt
Format an array as a human-readable list with conjunction words. This is the primary formatting method for arrays.
Usage: fmt(conjunction)
Format with "and" or "or" conjunction:
["Alice", "Bob", "Charlie"].fmt("and") // "Alice, Bob, and Charlie"
["coffee", "tea", "milk"].fmt("or") // "coffee, tea, or milk"
Usage: fmt(conjunction, locale)
Format with locale-aware conjunction words:
["Alice", "Bob", "Charlie"].fmt("and", "de-DE") // "Alice, Bob und Charlie"
["Alice", "Bob", "Charlie"].fmt("and", "fr-FR") // "Alice, Bob et Charlie"
["Alice", "Bob", "Charlie"].fmt("or", "de-DE") // "Alice, Bob oder Charlie"
["Alice", "Bob", "Charlie"].fmt("or", "es-ES") // "Alice, Bob o Charlie"
Edge Cases
[].fmt("and") // "" (empty string)
["Alice"].fmt("and") // "Alice" (no conjunction)
["Alice", "Bob"].fmt("and") // "Alice and Bob" (no comma)
["A", "B", "C"].fmt("and") // "A, B, and C" (Oxford comma)
format
Alias for fmt(). Retained for backward compatibility:
["Alice", "Bob", "Charlie"].format("and") // "Alice, Bob, and Charlie"
["coffee", "tea", "milk"].format("or") // "coffee, tea, or milk"
insert
Insert an element at a specific index, returning a new array. Supports negative indices:
["a", "c"].insert(1, "b") // ["a", "b", "c"]
["a", "b"].insert(-1, "x") // ["a", "x", "b"]
join
["Hello", "world"].join(" ") // "Hello world"
[1, 2, 3].join("-") // "1-2-3"
length
[10, 20, 30].length() // 3
map
[1, 2, 3].map(fn(x) { x * 2 }) // [2, 4, 6]
// Equivalent with for:
for (x in [1, 2, 3]) { x * 2 } // [2, 4, 6]
// Extract fields from dictionaries:
let users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
users.map(fn(u) { u.name }) // ["Alice", "Bob"]
pick
Select random elements with replacement (duplicates possible):
["red", "green", "blue"].pick(2) // e.g. ["green", "red"]
reduce
Accumulate a result across elements. Works naturally with Parsley's money type:
[1, 2, 3, 4].reduce(fn(sum, x) { sum + x }, 0) // 10
[£10.50, £5.25, £12.00].reduce(fn(total, p) { total + p }, £0.00) // £27.75
reorder
Reshape arrays of dictionaries — reorder, select, or rename keys in bulk. Useful for preparing database/API results for display or export:
With an array argument — select and reorder keys:
let users = [{name: "Alice", age: 30, city: "London"}, {name: "Bob", age: 25, city: "Paris"}]
users.reorder(["city", "name"])
Result: [{city: "London", name: "Alice"}, {city: "Paris", name: "Bob"}]
With a dictionary argument — rename and reorder:
let data = [{first_name: "Alice", last_name: "Smith"}, {first_name: "Bob", last_name: "Jones"}]
data.reorder({name: "first_name", surname: "last_name"})
Result: [{name: "Alice", surname: "Smith"}, {name: "Bob", surname: "Jones"}]
Non-dictionary elements in the array are left unchanged.
reverse
[1, 2, 3].reverse() // [3, 2, 1]
shuffle
Randomly reorder elements (Fisher-Yates):
[1, 2, 3, 4, 5].shuffle() // e.g. [3, 5, 1, 4, 2]
sort
Sort in natural order:
[3, 1, 4, 1, 5].sort() // [1, 1, 3, 4, 5]
["banana", "apple", "cherry"].sort() // ["apple", "banana", "cherry"]
⚠️ Natural sort order:
["10 banana", "9 apple", "100 cherry"].sort()→["9 apple", "10 banana", "100 cherry"]. Numbers embedded in strings are compared numerically, not lexicographically. This is almost always what you want but differs from most languages.
sortBy
Sort by a derived key. Sort is stable — equal elements preserve their original order:
let users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
users.sortBy(fn(u) { u.age })
// [{name: "Bob", age: 25}, {name: "Alice", age: 30}]
["hello", "a", "goodbye"].sortBy(fn(s) { s.length() })
// ["a", "hello", "goodbye"]
take
Select random elements without replacement (each picked at most once):
["♠", "♥", "♦", "♣"].take(2) // e.g. ["♥", "♣"]
toCSV
[["Name", "Age"], ["Alice", 30], ["Bob", 25]].toCSV()
Result:
Name,Age
Alice,30
Bob,25
toJSON
[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}].toJSON()
Result: [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
toBox
Render the array in a box with box-drawing characters. Useful for CLI output and debugging:
["apple", "banana", "cherry"].toBox()
Result:
┌────────┐
│ apple │
├────────┤
│ banana │
├────────┤
│ cherry │
└────────┘
Options
| Option | Type | Default | Description |
|---|---|---|---|
direction |
string | "vertical" |
Layout: "vertical", "horizontal", "grid" |
align |
string | "left" |
Text alignment: "left", "right", "center" |
style |
string | "single" |
Box border style: "single", "double", "ascii", "rounded" |
title |
string | none | Title row centered at top of box |
maxWidth |
integer | none | Truncate content to this width (adds ...) |
Direction Examples
[1, 2, 3].toBox({direction: "horizontal"})
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
└───┴───┴───┘
Grid layout (auto-detected for array of arrays):
[[1, 2, 3], [4, 5, 6]].toBox()
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
└───┴───┴───┘
Style Examples
["A", "B", "C"].toBox({style: "double", direction: "horizontal"})
╔═══╦═══╦═══╗
║ A ║ B ║ C ║
╚═══╩═══╩═══╝
Title Example
[1, 2, 3].toBox({title: "Numbers"})
┌─────────┐
│ Numbers │
├─────────┤
│ 1 │
├─────────┤
│ 2 │
├─────────┤
│ 3 │
└─────────┘
Examples
Chaining Operations
let people = [
{name: "Alice", age: 30},
{name: "Charlie", age: 22},
{name: "Bob", age: 28}
]
let adults = people
.filter(fn(p) { p.age > 25 })
.map(fn(p) { p.name })
.sort()
// ["Alice", "Bob"]
Money Arithmetic
let prices = [£10.00, £15.50, £8.25, £12.75]
let total = prices.reduce(fn(sum, p) { sum + p }, £0.00)
let average = total / prices.length()
// average = £11.63
Random Selection
let jedi = ["Yoda", "Luke", "Leia", "Obi-Wan", "Mace"]
let chosen = jedi.pick(1) // with replacement: e.g. ["Obi-Wan"]
let deck = ["2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♠"]
let hand = deck.shuffle().take(5) // without replacement: 5 distinct cards
Locale-Aware Presentation
let winners = ["Alice", "Bob", "Charlie"]
`Congratulations to {winners.format("and")}!`
// "Congratulations to Alice, Bob, and Charlie!"
// German:
`Herzlichen Glückwunsch {winners.format("and", "DE")}!`
Chunking for Layouts
let items = ["A", "B", "C", "D", "E", "F"]
let rows = items / 3
// [["A", "B", "C"], ["D", "E", "F"]]
// Render as a grid:
<table>
for (row in rows) {
<tr>for (cell in row) { <td>cell</td> }</tr>
}
</table>
Set Algebra
let admins = ["alice", "bob", "carol"]
let editors = ["bob", "carol", "dave"]
let both = admins && editors // ["bob", "carol"]
let either = admins || editors // ["alice", "bob", "carol", "dave"]
let adminsOnly = admins - editors // ["alice"]
Key Differences from Other Languages
| Concept | Parsley | Other languages |
|---|---|---|
for loops |
Return arrays (like map) |
Statements (no return value) |
++ |
Array concatenation | Not common (JS uses .concat()) |
/ on arrays |
Chunking | Not available |
&& || - on arrays |
Set intersection, union, subtraction | Not available |
| Sort order | Natural sort ("file2" < "file10") |
Lexicographic |
[?n] |
Optional access (returns null) |
Throws error |
pick / take |
Built-in random sampling | Requires library |
format |
Locale-aware list formatting | Requires library |
stop / skip |
Loop control | break / continue |
See Also
- Dictionaries — key-value pairs; arrays of dictionaries form tables
- Strings —
.split()returns arrays;.join()concatenates array elements - Control Flow —
forloops return arrays - Operators —
++concatenation,*repetition,/chunking - Types — array in the type system
- @std/math —
sum,avg,median, and other aggregation functions - Data Formats — CSV parsing returns arrays/tables