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] returns null instead 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. an if with no else), that element is omitted from the result β€” this is how filtering works. Use stop and skip instead of break and continue.

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