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.
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/table β SQL-like operations on arrays of dictionaries
- @std/math β
sum,avg,median, and other aggregation functions - Data Formats β CSV parsing returns arrays/tables