Arrays & Objects

OcaltQL has three structured data types: Array — a flat ordered list with numeric indices. Multiarray — an array of arrays for nested data. Object — a key-value structure with string keys. All three share consistent verbs for access, search, mutation, and conversion.

Arrays

An array is a flat ordered list. Indices are zero-based numbers. Items are appended sequentially and repack automatically when removed.

Create and Populate
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
Access by Index
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER EMIT ?arr(0)
AFTER EMIT ?arr(3)
(* Output: alphadelta *)
COUNT — Number of Items
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER COUNT ?arr SET ?num
AFTER EMIT ?num
(* Output: 4 *)
REMOVE KEY — Delete by Index
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER REMOVE KEY 0 FROM ?arr SET ?arr
(* ?arr is now: beta(0), gamma(1), delta(2) — repacked automatically *)
REVERSE ARRAY
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER REVERSE ARRAY ?arr SET ?rev
AFTER EMIT ?rev(0)
(* Output: delta *)
SEARCH ARRAY — Found
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER SEARCH ARRAY ?arr FOR "beta" SET ?found
AFTER EMIT ?found(0)
(* Output: 1 — index of "beta" in the array *)
SEARCH ARRAY — Not Found
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER SEARCH ARRAY ?arr FOR "zulu" SET ?notfound
AFTER EMIT ?notfound
(* Output: null *)
JOIN — With Separator
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER JOIN ?arr WITH ", " SET ?csv
AFTER EMIT ?csv
(* Output: alpha, beta, gamma, delta *)
JOIN — No Separator
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER JOIN ?arr SET ?flat
AFTER EMIT ?flat
(* Output: alphabetagammadelta — no separator *)

JOIN ARRAY ?arr ... is an accepted alias for JOIN ?arr ... — both forms are equivalent.

SLICE — Subset by Range
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER SLICE ?arr FROM 1 TO 2 SET ?sub
AFTER EMIT ?sub(0)
(* Output: beta *)
SORT
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER APPEND "gamma" TO ?arr
AFTER APPEND "delta" TO ?arr
AFTER SORT ?arr SET ?sorted
MERGE ARRAY
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER NEW ARRAY SET ?more
AFTER APPEND "echo" TO ?more
AFTER MERGE ARRAY ?arr WITH ?more SET ?combined
DEDUPLICATE — Remove Duplicate Values
NEW ARRAY SET ?dupes
AFTER APPEND "a" TO ?dupes
AFTER APPEND "b" TO ?dupes
AFTER APPEND "a" TO ?dupes
AFTER APPEND "c" TO ?dupes
AFTER APPEND "b" TO ?dupes
AFTER DEDUPLICATE ?dupes SET ?unique
AFTER EMIT ?unique(0)
AFTER EMIT ?unique(1)
AFTER EMIT ?unique(2)
AFTER COUNT ?unique SET ?count
AFTER EMIT ?count
(* Output: abc3 *)

Multiarrays

A multiarray is an array of arrays. It is used for nested or grid-like data structures. Rows are arrays appended to the multiarray. Both () notation and OF chain notation are valid for access.

Create a Multiarray
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND 6 TO ?row2
AFTER APPEND ?row2 TO ?matrix
Access Nested — () Notation
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND 6 TO ?row2
AFTER APPEND ?row2 TO ?matrix
AFTER EMIT ?matrix(0)(0)
AFTER EMIT ?matrix(1)(2)
(* Output: 16 *)
Access Nested — OF Chain
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND 6 TO ?row2
AFTER APPEND ?row2 TO ?matrix
AFTER EMIT 0 OF 0 OF ?matrix
AFTER EMIT 2 OF 1 OF ?matrix
(* Output: 16 — identical result to () notation *)
COUNT Outer — Number of Rows
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND 6 TO ?row2
AFTER APPEND ?row2 TO ?matrix
AFTER COUNT ?matrix SET ?rows
AFTER EMIT ?rows
(* Output: 2 *)
COUNT Inner — Number of Columns in a Row
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER COUNT ?matrix(0) SET ?cols
AFTER EMIT ?cols
(* Output: 3 *)
REMOVE Nested — () Notation
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER REMOVE KEY 1 FROM ?matrix(0) SET ?matrix
(* Row 0 is now 1, 3 — repacked to indices 0, 1 *)
REMOVE Nested — OF Chain
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER REMOVE KEY 1 FROM 0 OF ?matrix SET ?matrix
(* Row 0 is now 1, 3 — identical result to () notation *)
REVERSE Outer — Row Order
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND ?row2 TO ?matrix
AFTER REVERSE ARRAY ?matrix SET ?revrows
REVERSE Inner — A Single Row in Place
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER REVERSE ARRAY ?matrix(0) SET ?matrix
FLATTEN — Multiarray to Flat Array
NEW MULTIARRAY SET ?matrix
AFTER NEW ARRAY SET ?row1
AFTER APPEND 1 TO ?row1
AFTER APPEND 2 TO ?row1
AFTER APPEND 3 TO ?row1
AFTER APPEND ?row1 TO ?matrix
AFTER NEW ARRAY SET ?row2
AFTER APPEND 4 TO ?row2
AFTER APPEND 5 TO ?row2
AFTER APPEND 6 TO ?row2
AFTER APPEND ?row2 TO ?matrix
AFTER FLATTEN ?matrix SET ?flat
AFTER EMIT ?flat(0)
AFTER EMIT ?flat(3)
(* Output: 14 — first element of row 0, first element of row 1 *)

Objects

An object holds key-value pairs with string keys. Properties are set with SET ?obj("key") AS value. Both () notation and OF chain notation are valid throughout.

Create and Populate
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("age") AS 30
AFTER SET ?user("active") AS true
Access — () Notation
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER EMIT ?user("name")
(* Output: Alice *)
Access — OF Chain
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER EMIT "name" OF ?user
(* Output: Alice — identical result to () notation *)
COUNT Properties
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("age") AS 30
AFTER SET ?user("active") AS true
AFTER COUNT ?user SET ?propcount
AFTER EMIT ?propcount
(* Output: 3 *)
Replace a Property Value — () Notation
NEW OBJECT SET ?user
AFTER SET ?user("age") AS 30
AFTER SET ?user("age") AS 31
Replace a Property Value — OF Chain
NEW OBJECT SET ?user
AFTER SET ?user("age") AS 30
AFTER SET "age" OF ?user AS 32
REMOVE KEY — Delete a Property
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("active") AS true
AFTER REMOVE KEY "active" FROM ?user SET ?user
(* "active" is gone entirely — no null, no gap *)
KEYS
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("age") AS 32
AFTER KEYS ?user SET ?props
AFTER EMIT ?props(0)
AFTER EMIT ?props(1)
(* Output: nameage *)
VALUES
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("age") AS 32
AFTER VALUES ?user SET ?vals
AFTER EMIT ?vals(0)
AFTER EMIT ?vals(1)
(* Output: Alice32 *)
APPEND to Object — Add Key-Value Pair
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER APPEND "role" AS "admin" TO ?user
(* ?user now has "role" = "admin" *)
SEARCH OBJECT — Found
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SEARCH OBJECT ?user FOR "Alice" SET ?foundkey
AFTER EMIT ?foundkey(0)
(* Output: "name" *)
SEARCH OBJECT — Not Found
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SEARCH OBJECT ?user FOR "zulu" SET ?nokey
AFTER EMIT ?nokey
(* Output: null *)
MERGE OBJECT — Right Overwrites Left on Collision
NEW OBJECT SET ?defaults
AFTER SET ?defaults("theme") AS "dark"
AFTER SET ?defaults("lang") AS "en"
AFTER NEW OBJECT SET ?prefs
AFTER SET ?prefs("lang") AS "af"
AFTER MERGE OBJECT ?defaults WITH ?prefs SET ?config
(* ?config("theme") = "dark" *)
(* ?config("lang")  = "af"   — right side wins *)

Nested Objects

Object properties can hold other objects. Nested access, mutation, and removal all follow the same two-notation pattern.

Nested Object — Create and Access
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER SET ?addr("country") AS "South Africa"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER EMIT ?user("address")("city")
(* Output: Johannesburg *)
Nested Object — Access via OF Chain
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER EMIT "city" OF "address" OF ?user
(* Output: Johannesburg — identical result to () notation *)
Nested Object — Replace, () Notation
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER SET ?user("address")("city") AS "Cape Town"
Nested Object — Replace, OF Chain
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER SET "city" OF "address" OF ?user AS "Cape Town"
Nested Object — Remove a Nested Property
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER SET ?addr("country") AS "South Africa"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER REMOVE KEY "country" FROM ?user("address") SET ?user
Nested Object — Keys of a Nested Object
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER SET ?addr("country") AS "South Africa"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER KEYS ?user("address") SET ?addrprops

Copying via Variable-to-Variable Assignment

The SET ... AS and VARIABLE ... SET forms documented on Variables SET | UNSET work identically on arrays and objects, not just plain values.

Copying an Array — SET ... AS Form
NEW ARRAY SET ?arr1
AFTER APPEND "alpha" TO ?arr1
AFTER SET ?arr2 AS ?arr1
AFTER EMIT ?arr2(0)
Copying an Array — VARIABLE ... SET Form
NEW ARRAY SET ?arr1
AFTER APPEND "alpha" TO ?arr1
AFTER VARIABLE ?arr1 SET ?arr2
AFTER EMIT ?arr2(0)
Copying an Object — SET ... AS Form
NEW OBJECT SET ?user1
AFTER SET ?user1("name") AS "Alice"
AFTER SET ?user2 AS ?user1
AFTER EMIT ?user2("name")
Copying an Object — VARIABLE ... SET Form
NEW OBJECT SET ?user1
AFTER SET ?user1("name") AS "Alice"
AFTER VARIABLE ?user1 SET ?user2
AFTER EMIT ?user2("name")

Object Literals — Inline Creation

An object can be built in one statement instead of a bare NEW OBJECT followed by separate SET calls. Square brackets directly after NEW OBJECT hold the key/value pairs, joined with AND.

Inline object
NEW OBJECT ["name" AS "Alice" AND "age" AS 30] SET ?user
AFTER EMIT ?user("name")
AFTER EMIT " / "
AFTER EMIT ?user("age")
(* Output: Alice / 30 *)

A nested object as a value keeps the NEW OBJECT keyword and its own brackets — written directly after AS, with no extra wrapping needed.

Nested inline object
NEW OBJECT ["name" AS "Alice" AND "address" AS NEW OBJECT ["city" AS "Johannesburg"]] SET ?user
AFTER EMIT ?user("address")("city")
(* Output: Johannesburg *)

A bare NEW OBJECT with no brackets still creates an empty object, exactly as before — keys can be added afterward with SET.

Empty object, keys added afterward
NEW OBJECT SET ?empty
AFTER SET ?empty("x") AS "y"
AFTER EMIT ?empty("x")
(* Output: y *)

Native Object Storage

Internally, an object’s canonical stored form is valid OcaltQL source text — not a JSON-style format. An object written to storage is written as the literal NEW OBJECT [...] body wrapped in one more set of brackets — [NEW OBJECT [...]] — with no SET, since the value is only captured when something actually consumes it. This means reading a stored object back is not a separate deserialization step — the stored text is simply re-evaluated as an inline expression through the same parser that runs any other script.

This applies uniformly regardless of an object’s origin. An object parsed from JSON, YAML, XML, or TOML via PARSE becomes the exact same underlying object type as one built with NEW OBJECT — there is no lingering distinction based on source format. Format only matters again at the moment of serialization, using CAST ?obj AS OQLOBJECT or CAST ?obj AS JSON to choose the output form explicitly. See Convert & Cast for the full CAST and PARSE reference.

FOREACH — Iteration

FOREACH iterates over arrays and objects. It exposes the value alone, or both the key and value together.

FOREACH Array — Value Only
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER FOREACH ?arr SET ?value
OPEN
  EMIT ?value
CLOSE
FOREACH Array — Key and Value
NEW ARRAY SET ?arr
AFTER APPEND "alpha" TO ?arr
AFTER APPEND "beta" TO ?arr
AFTER FOREACH ?arr SET ?key AS ?value
OPEN
  EMIT ?key & ": " & ?value
CLOSE
FOREACH Object — Key and Value
NEW OBJECT SET ?user
AFTER SET ?user("name") AS "Alice"
AFTER SET ?user("age") AS 30
AFTER FOREACH ?user SET ?key AS ?value
OPEN
  EMIT ?key & " = " & ?value
CLOSE
FOREACH Nested Object — () Notation
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER SET ?addr("country") AS "South Africa"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER FOREACH ?user("address") SET ?key AS ?value
OPEN
  EMIT ?key & ": " & ?value
CLOSE
FOREACH Nested Object — OF Chain
NEW OBJECT SET ?addr
AFTER SET ?addr("city") AS "Johannesburg"
AFTER SET ?addr("country") AS "South Africa"
AFTER NEW OBJECT SET ?user
AFTER SET ?user("address") AS ?addr
AFTER FOREACH "address" OF ?user SET ?key AS ?value
OPEN
  EMIT ?key & ": " & ?value
CLOSE
COUNT is for arrays and objects. LENGTH is for strings. They are not interchangeable.