Collections: Arrays, Maps and Unicode Text Manipulation
Before: Functions and closures. After: Type system and inference. Technical reference: Native modules, Global functions.
What are you going to learn here?
So far we have worked with variables that store a single piece of data at a time: a number, a name, or a boolean. But in real life data almost never comes isolated: - A list of products in an online store. - Comments on a post. - A user’s file with their name, email, telephone number and address.
To group and organize multiple data in a single structure, there are collections.
In this guide you will learn:
1. What is an array, how numbering works from scratch (indexes) and how to add elements.
2. What is a map (key-value dictionary) with curly brace syntax {} or PHP associative style ["clave" => valor].
3. How to traverse collections with foreach extracting pairs $clave => $valor.
4. How to process lists with functional functions and pipelines: map, filter, reduce, find, any, all, sum.
5. How collections behave in memory: reference copying vs data duplication.
6. The peculiarities of functions like array_pop, array_push and array_shift in Joss.
7. Text as a collection: the fundamental difference between bytes, Unicode code points and graphemes (visible characters).
1. Arrays: Ordered sequences of elements
An array is an ordered list of values. Each value occupies a numbered box called index.
In Joss (and in the vast majority of modern languages), indexes start counting from zero (0), not from one:
- The first element is in the index 0.
- The second element is at the index 1.
- The third element is in the index 2.
$compras = ["pan", "leche"]
print($compras[0])
$compras[] = "fruta"
print(count($compras))
print($compras[2])
Basic operations with arrays:
- Creation: They are delimited with square brackets
[and], separating the elements by commas:["pan", "leche"]. - Read by index:
$compras[0]accesses the first element ("pan"). - Add to end with
[]: Typing$compras[] = "fruta"automatically adds the new element to the end of the list. - Count elements:
count($compras)(orlen($compras)) returns the total number of elements (in this case,3). - Boundary Protection: If you try to access an index that does not exist (for example
$compras[99]or a negative index$compras[-1]), Joss stops execution immediately with the security errorJOSS-INDEX-001(Index Out of Range), protecting your program from reading junk memory.
Collection Typing: Homogeneous Arrays
By default, an array array can contain mixed types. If you want to guarantee that all elements are integers, you can use the parameterized syntax:
array<int> $edades = [18, 25, 30]
Declarative destructuring of arrays and maps
When you have an array or a map and you need to extract its elements into separate variables, you don’t need to write repetitive individual assignments. You can unpack them directly into a single line using declarative destructuring, including optional default values:
[$x, $y, $z = 30] = [10, 20]
print($x)
print($y)
print($z)
{"nombre": $nombre, "rol": $rol = "cliente"} = {"nombre": "Ada"}
print($nombre)
print($rol)
This is very convenient for unpacking web request parameters, coordinate pairs, or results returned by functions without ceremonial code.
Spread operator (...) on arrays
You can expand the elements of an existing array inside another new array prepending ...:
$primeros = [2, 3]
$todos = [1, ...$primeros, 4]
print($todos[0])
print($todos[1])
print($todos[2])
print($todos[3])
2. Maps: Key → Value Dictionaries
Un array is perfect when the order of the elements matters (like a waiting list). But if you want to represent an entity with tagged properties (like a user), remembering that “name is at index 0 and mail is at index 1” is brittle and confusing.
That’s why maps exist (also known as dictionaries, hash tables or associative maps). In a map, each value is saved and retrieved using a text key:
$persona = {"nombre": "Ada", "edad": 20}
print($persona["nombre"])
$persona["edad"] = 21
print($persona["edad"])
print($persona["telefono"] ?? "sin teléfono")
Features of Maps in Joss:
- Creation: They are delimited with braces
{}associating with:({"clave": valor}) or with square brackets[]associating with=>(["clave" => valor]). - Empty map:
{}creates an empty map. - Access and modification: Brackets are used with the name of the key in quotes:
$persona["nombre"]. - Non-existent keys: If you try to read a key that does not exist (such as
$persona["telefono"]), Joss safely returnsnullinstead of failing. You can use the??operator to provide an elegant default value. - Existence check: You can use
array_key_exists("telefono", $persona)to know for sure if a key was defined, even if its associated value isnull.
PHP-style associative notation (=>)
Joss supports both the classic curly brace notation {} with colon : as the square bracket notation and thick arrow =>, including nested multidimensional maps:
$config = [
"app" => "JosSecurity",
"version" => "v1.0",
"autor" => ["nombre" => "Ada"]
]
print($config["app"])
print($config["version"])
print($config["autor"]["nombre"])
Spread operator (...) in maps
Just like in arrays, you can spread and merge key-value pairs of one map inside another using the operator ...:
$base = ["host" => "localhost", "puerto" => "8080"]
$completo = [...$base, "seguro" => "true"]
print($completo["host"])
print($completo["puerto"])
print($completo["seguro"])
3. Browse Collections with foreach ($coleccion as $clave => $valor)
You can traverse arrays and associative maps by directly accessing the key (or numeric index in arrays) and the value using the syntax $clave => $valor:
$frutas = ["manzana", "pera"]
foreach ($frutas as $indice => $fruta) {
print($indice . ": " . $fruta)
}
$letras = ["a" => "alfa", "b" => "beta"]
foreach ($letras as $k => $v) {
print($k . " => " . $v)
}
keys($persona): Returns an array with all the names of the map keys.values($persona): Returns an array with only the values contained in the map.
4. Higher order functions and Pipelines (map, filter, reduce, find, sum)
Joss includes built-in functions to transform and filter collections in functional style, designed to be chained with the pipeline operator |>:
$numeros = [1, 2, 3, 4, 5]
$pares = $numeros |> filter(func(int $x): bool { return $x % 2 == 0; })
$escalados = $pares |> map(func(int $x): int { return $x * 10; })
print(sum($escalados))
$encontrado = $numeros |> find(func(int $x): bool { return $x == 2; })
print($encontrado * 10)
print($numeros |> any(func(int $x): bool { return $x == 3; }))
Fluent instance methods on collections and strings
In addition to the pipeline operator, Joss allows calling fluid methods directly on primitive values (string, array, map):
$txt = " Hola Mundo "
print($txt->trim()->lower()->replace(" ", "-"))
$nums = [1, 2, 3, 4]
$filtrados = $nums->map(func(int $n, int $i): int { return $n * 2; })->filter(func(int $n, int $i): bool { return $n > 4; })
print($filtrados->join(", "))
$mapa = {"a": 1, "b": 2}
print($mapa->keys()->join("-"))
5. In-memory behavior: Copy or reference?
This is a fundamental concept in the Joss architecture:
- When you assign a number or text to another variable (
$b = $a), the value is copied independently. - Instead, arrays and maps are managed internally using pointers and shared structures (Go slices and maps).
If you assign an existing array or map to a new variable, both variables point to the same data structure in memory:
$original = [1, 2]
$copia = $original
$copia[0] = 9
print($original[0])
$datos = {"estado": "inicial"}
$alias = $datos
$alias["estado"] = "nuevo"
print($datos["estado"])
Modifying $copia[0] also altered $original[0], because both are two different names for the same physical array.
[!TIP] How to create a separate copy? To duplicate an array without sharing future changes, use the function
merge:
$clon = merge([], $original)
6. Function names that you should know well
Some functions to manipulate arrays have Specific contracts in Joss that differ from languages like PHP or JavaScript:
$numeros = [1, 2]
print(array_pop($numeros))
print(count($numeros))
$numeros = array_push($numeros, 3)
print(count($numeros))
Pay attention to these details:
1. array_pop($arr): Returns the last element, but does not remove it from the original array (does not mutate the length).
2. array_shift($arr): Returns the first element without deleting it.
3. array_push($arr, $item) and append($arr, $item): They take the array, add the new element to it and return the new resulting array. That’s why you should remap: $numeros = array_push($numeros, 3) or use the direct syntax $numeros[] = 3.
4. array_slice($arr, $inicio, $longitud): Extracts a portion of the array without modifying the original.
7. Text Manipulation: Bytes, Runes and Graphemes
Modern digital text is much more complex than the English letters on the ASCII keyboard. When you handle text with accents (á, é), Asian characters, or emojis (😀, 👨👩👧👦), a single visual letter can be made up of multiple bytes and even multiple Unicode characters combined.
In Joss:
$texto = "é"
print(len($texto))
print(strlen($texto))
print($texto[0])
Observe the difference of the three lines for the letter é (letter e with combined accent):
1. len($texto): Returns 3 bytes in physical UTF-8 format.
2. strlen($texto): Returns 2 Unicode code points (the base e + the combining accent).
3. $texto[0]: Returns the complete visual grapheme (é).
| Operation | Measuring unit | Recommended use |
|---|---|---|
len($texto) |
Physical bytes | File sizes, network transfers, disk buffers. |
strlen($texto) |
Code Points (Runes) | Standard text analysis algorithms. |
$texto[$i] |
Extended graphemes (visible characters) | User-oriented text manipulation: cut names, show avatars, index without breaking emojis in half. |
7. JSON Serialization and Deserialization (json_encode and json_decode)
In Joss you can serialize and deserialize JSON using both maps notation with braces {} and associative arrays notation with brackets [] (=>), in addition to conventional indexed arrays. Both formats are supported at json_encode and json_decode (or JSON::encode and JSON::decode):
With maps using braces {}:
$perfil = {
"nombre": "Ada",
"lenguajes": ["Joss", "Go"]
}
$jsonMapa = json_encode($perfil)
$datosMapa = json_decode($jsonMapa)
print($datosMapa["nombre"])
print($datosMapa["lenguajes"][0])
With associative fixes using square brackets [] and =>:
$usuario = [
"nombre" => "Carlos",
"roles" => ["admin", "editor"]
]
$jsonArray = json_encode($usuario)
$datosArray = json_decode($jsonArray)
print($datosArray["nombre"])
print($datosArray["roles"][0])
json_encode($datos, $pretty = false): Converts arrays, maps or instances into a valid JSON string. Passingtruein the second parameter formats the text with readable indentation.json_decode($cadenaJson): Converts a JSON string to Joss collections:{}objects are deserialized to associative mapsmap, lists[]in arrays and integers preserve the typeint.json_verify($cadenaJson): Validates whether the text contains a syntactically correct JSON structure.
8. Summary of useful functions for collections
| Function | Purpose | Example |
|---|---|---|
count($arr) / len($arr) |
Returns the length of a collection or text. | count([1, 2]) → 2 |
is_array($val) |
Checks if a value is an array or associative map. | is_array(["a" => 1]) → true |
in_array($val, $arr) |
Checks if a value exists in the array. | in_array(2, [1, 2, 3]) → true |
keys($map) |
Gets the list of keys for a map. | keys({"a": 1}) → ["a"] |
values($map) |
Gets the list of values for a map. | values({"a": 1}) → [1] |
explode($sep, $str) |
Splits text into an array using a separator. | explode(",", "a,b,c") → ["a", "b", "c"] |
implode($sep, $arr) |
Joins an array of texts into a single string. | implode("-", ["2026", "09", "05"]) → "2026-09-05" |
json_encode($val) |
Converts an array or map to JSON text. | json_encode(["ok" => true]) → '{"ok":true}' |
json_decode($str) |
Converts JSON text to Joss array or map. | json_decode('{"ok":true}') |
array_reverse($arr) |
Reverse the order of the elements. | array_reverse([1, 2, 3]) → [3, 2, 1] |
9. Practical exercises
- Inventory Management:
- Create a map called
$productowith the keys"nombre"("Laptop"),"precio"(1200.00m) and"stock"(5). - Sample in terminal:
"Producto: Laptop | Precio: $1200.00 | Disponibles: 5". - Simulates a purchase by subtracting 1 from the stock and shows the new value.
- Filter words with
explodeandimplode:
- Create a text
$frase = "manzana,pera,uva,platano". - Convert it to an array using
explode. - Loop through the array with
foreachand print each fruit in uppercase using the pipeline operator:$fruta |> strtoupper.
Next step
Now that you’ve mastered in-memory data structures, it’s time to dive deeper into how the Joss type parser verifies contracts, how inference works, and how null values are safely handled.
Continues with: Type system, inference and conversions.