🗂️ Data Structures Across Languages
Every backend you write spends most of its time shuffling data in and out of a handful of built-in containers. In this lesson you'll learn the four core structures — ordered lists, keyed maps, unique sets, and fixed tuples — and see exactly how JavaScript, Python, and PHP each spell them. Learn the shapes once and you'll read code in all three languages fluently.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Map the same four data structures — lists, maps, sets, tuples — onto their JavaScript, Python, and PHP equivalents
- Perform common operations (add, access, iterate, transform) on each, in each language
- Use map / filter / reduce, list comprehensions, and PHP's array functions to transform collections without manual loops
- Choose the right structure for a task based on ordering, keys, and uniqueness
Estimated Time: 30–40 minutes • Difficulty: Beginner–Intermediate
Hands-on: Rewrite the same "tally the votes" task in all three languages using the idiomatic structure and transform for each.
In This Lesson
Four Shapes, Three Languages
Underneath the syntax, almost every collection you'll ever use is one of four shapes. Nail these four and the differences between JavaScript, Python, and PHP become vocabulary, not concepts.
| Shape | JavaScript | Python | PHP |
|---|---|---|---|
| Ordered list | Array | list | array (indexed) |
| Keyed map | Object / Map | dict | array (associative) |
| Unique set | Set | set | array + array_unique |
| Fixed tuple | array (by convention) | tuple | array (by convention) |
📖 The PHP twist
PHP has one workhorse type — the array — that is both an ordered list and a keyed map at the same time. An indexed array is just an associative array whose keys happen to be 0, 1, 2…. This is different from JS and Python, which keep lists and maps as distinct types.
Ordered Lists: Arrays & Lists
A list holds values in order and lets you reach any of them by a zero-based index. This is the structure you reach for whenever sequence matters — a queue of jobs, rows from a query, items in a cart.
Creating, accessing, and growing
// JavaScript
const fruits = ['apple', 'banana', 'cherry'];
fruits[0]; // 'apple'
fruits.length; // 3
fruits.push('date'); // add to the end -> length 4
fruits.pop(); // remove from the end -> 'date'
fruits.includes('banana'); // true
# Python
fruits = ['apple', 'banana', 'cherry']
fruits[0] # 'apple'
len(fruits) # 3
fruits.append('date') # add to the end
fruits.pop() # remove from the end -> 'date'
'banana' in fruits # True
<?php
// PHP
$fruits = ['apple', 'banana', 'cherry'];
$fruits[0]; // 'apple'
count($fruits); // 3
$fruits[] = 'date'; // add to the end
array_pop($fruits); // remove from the end -> 'date'
in_array('banana', $fruits, true); // true
⚠️ Off-by-one and negative indexes
All three languages are zero-based: the first element is index 0. But only Python supports negative indexes out of the box — fruits[-1] is the last item. In JavaScript use fruits.at(-1), and in PHP use end($fruits) or array_key_last().
Slicing a range
// JavaScript — slice(start, end) — end is exclusive
const nums = [10, 20, 30, 40, 50];
nums.slice(1, 3); // [20, 30]
# Python — nums[start:end] — end is exclusive
nums = [10, 20, 30, 40, 50]
nums[1:3] # [20, 30]
<?php
// PHP — array_slice(array, offset, length)
$nums = [10, 20, 30, 40, 50];
array_slice($nums, 1, 2); // [20, 30]
Keyed Maps: Objects, Dicts & Associative Arrays
A map stores values under named keys instead of numeric positions. It's the natural shape for a record: a user with a name, email, and role. Look-ups by key are fast and read clearly.
// JavaScript — a plain object for string keys
const user = { name: 'Ada', age: 36, role: 'engineer' };
user.name; // 'Ada' (dot access)
user['age']; // 36 (bracket access)
user.email = 'a@x.io'; // add a key
delete user.age; // remove a key
'role' in user; // true
Object.keys(user); // ['name', 'role', 'email']
// For non-string keys or frequent add/remove, prefer Map:
const scores = new Map();
scores.set('ada', 42);
scores.get('ada'); // 42
scores.has('ada'); // true
# Python — dict
user = {'name': 'Ada', 'age': 36, 'role': 'engineer'}
user['name'] # 'Ada'
user.get('email', 'n/a') # safe access with a default
user['email'] = 'a@x.io' # add a key
del user['age'] # remove a key
'role' in user # True
list(user.keys()) # ['name', 'role', 'email']
<?php
// PHP — an associative array
$user = ['name' => 'Ada', 'age' => 36, 'role' => 'engineer'];
$user['name']; // 'Ada'
$user['email'] = 'a@x.io'; // add a key
unset($user['age']); // remove a key
array_key_exists('role', $user); // true
array_keys($user); // ['name', 'role', 'email']
💡 Object vs Map in JavaScript
Reach for a plain object for fixed, known, string-keyed records (like config or a parsed JSON row). Reach for Map when keys are dynamic, non-string, or the collection changes size a lot — Map preserves insertion order, offers a real .size, and iterates cleanly with for…of.
Insertion order is preserved in all three modern runtimes: JavaScript objects (for string keys), Map, Python dict (guaranteed since 3.7), and PHP arrays all remember the order you added keys.
Sets & Tuples
Sets — collections of unique values
A set automatically discards duplicates and answers "is this in here?" quickly. Perfect for tags, unique visitor IDs, or de-duplicating a list.
// JavaScript — Set
const tags = new Set(['red', 'green', 'red']);
tags.size; // 2 (duplicate dropped)
tags.add('blue');
tags.has('green'); // true
[...tags]; // ['red', 'green', 'blue'] -> back to an array
// De-duplicate an array in one line:
const unique = [...new Set([1, 1, 2, 3, 3])]; // [1, 2, 3]
# Python — set
tags = {'red', 'green', 'red'}
len(tags) # 2
tags.add('blue')
'green' in tags # True
list({1, 1, 2, 3, 3}) # [1, 2, 3] -> de-duplicated
# Sets support real set math:
{1, 2, 3} & {2, 3, 4} # intersection -> {2, 3}
{1, 2, 3} | {3, 4} # union -> {1, 2, 3, 4}
<?php
// PHP has no dedicated Set type — use array_unique or keys
$tags = array_unique(['red', 'green', 'red']); // ['red', 'green']
in_array('green', $tags, true); // true
// A common trick: use array keys as a set (values become true)
$seen = [];
$seen['red'] = true;
isset($seen['red']); // true
Tuples — fixed, positional groups
A tuple bundles a small, fixed number of related values whose positions carry meaning — like (latitude, longitude) or a (row, column) pair. Only Python has a true immutable tuple type; JS and PHP use short arrays by convention.
# Python — a real, immutable tuple
point = (51.5, -0.12)
lat, lng = point # unpacking into two variables
point[0] # 51.5
# point[0] = 0 # TypeError — tuples cannot be changed
// JavaScript — a fixed-length array + destructuring
const point = [51.5, -0.12];
const [lat, lng] = point; // lat = 51.5, lng = -0.12
Object.freeze(point); // opt-in immutability
<?php
// PHP — a short array + list() destructuring
$point = [51.5, -0.12];
[$lat, $lng] = $point; // $lat = 51.5, $lng = -0.12
Transforming Collections
The real productivity boost comes from transforming a whole collection at once — instead of writing manual loops with a counter. The three big transforms are map (change every item), filter (keep some items), and reduce (fold everything into one value). Each language spells them differently but the intent is identical.
map — transform every element
// JavaScript
const nums = [1, 2, 3, 4];
const squares = nums.map(x => x * x); // [1, 4, 9, 16]
# Python — a list comprehension is the idiomatic "map"
nums = [1, 2, 3, 4]
squares = [x * x for x in nums] # [1, 4, 9, 16]
<?php
// PHP
$nums = [1, 2, 3, 4];
$squares = array_map(fn($x) => $x * $x, $nums); // [1, 4, 9, 16]
filter — keep only what matches
// JavaScript
const big = [1, 4, 9, 16].filter(x => x > 5); // [9, 16]
# Python — comprehension with an "if" clause
big = [x for x in [1, 4, 9, 16] if x > 5] # [9, 16]
<?php
// PHP — array_filter keeps elements where the callback is truthy
$big = array_filter([1, 4, 9, 16], fn($x) => $x > 5); // [9, 16]
$big = array_values($big); // re-index keys 0,1,... if needed
⚠️ PHP: filter preserves keys
array_filter keeps the original keys, so a filtered indexed array can end up with gaps like [1 => 4, 3 => 16]. Wrap it in array_values() when you need a clean 0, 1, 2… sequence again.
reduce — fold into a single value
// JavaScript — reduce(callback, initialValue)
const total = [9, 16].reduce((sum, x) => sum + x, 0); // 25
# Python — sum() is built in; functools.reduce for the general case
total = sum([9, 16]) # 25
from functools import reduce
total = reduce(lambda acc, x: acc + x, [9, 16], 0) # 25
<?php
// PHP — array_reduce(array, callback, initial)
$total = array_reduce([9, 16], fn($acc, $x) => $acc + $x, 0); // 25
// or simply: array_sum([9, 16]);
Iterating over a map
To loop over keys and values together:
// JavaScript
const user = { name: 'Ada', role: 'engineer' };
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}
# Python
user = {'name': 'Ada', 'role': 'engineer'}
for key, value in user.items():
print(f'{key}: {value}')
<?php
// PHP
$user = ['name' => 'Ada', 'role' => 'engineer'];
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
✅ Chaining reads top-to-bottom
In JavaScript you can chain: rows.filter(isActive).map(toName).join(', '). Python favours a single comprehension: [to_name(r) for r in rows if is_active(r)]. PHP nests function calls: array_map($toName, array_filter($rows, $isActive)) — read those from the inside out.
Choosing the Right Structure
Ask three questions and the answer falls out almost every time:
| Need | Reach for | Why |
|---|---|---|
| Order matters, look up by position | List / Array | Fast indexed access; keeps sequence |
| Look up by a name or id | Map | Direct key access, self-documenting |
| Membership & no duplicates | Set | Automatic de-duplication, fast "contains" |
| Fixed group of related values | Tuple | Positions carry meaning; often immutable |
💡 Rule of thumb: If you find yourself writingif (!list.includes(x)) list.push(x), you actually wanted a Set. If you're searching an array to find the item with a matchingid, you probably wanted a Map keyed by that id.
Hands-on Exercise
🏋️ Tally the Votes
Objective: Given a list of votes, count how many each option received and return the tallies as a map. Do it idiomatically in all three languages.
Input: ['cat', 'dog', 'cat', 'bird', 'dog', 'cat']
Expected output (a map): cat: 3, dog: 2, bird: 1
Steps:
- Start with an empty map (object / dict / associative array).
- Loop the votes; for each, add 1 to that key's running count (default to 0 if unseen).
- Return the map. Bonus: also produce the list of unique options using a Set.
💡 Hint
The tricky part is the "default to 0 for a key you haven't seen yet." In JS use tally[v] ?? 0; in Python use tally.get(v, 0) or collections.Counter; in PHP a missing key read with ?? gives you 0 too: $tally[$v] ?? 0.
✅ Solution
// JavaScript
const votes = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat'];
const tally = {};
for (const v of votes) {
tally[v] = (tally[v] ?? 0) + 1;
}
// { cat: 3, dog: 2, bird: 1 }
const options = [...new Set(votes)]; // ['cat', 'dog', 'bird']
# Python
from collections import Counter
votes = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat']
tally = Counter(votes) # {'cat': 3, 'dog': 2, 'bird': 1}
options = set(votes) # {'cat', 'dog', 'bird'}
# Without Counter, the explicit version:
tally = {}
for v in votes:
tally[v] = tally.get(v, 0) + 1
<?php
// PHP — array_count_values does exactly this
$votes = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat'];
$tally = array_count_values($votes); // ['cat'=>3,'dog'=>2,'bird'=>1]
$options = array_values(array_unique($votes)); // ['cat','dog','bird']
// The explicit version:
$tally = [];
foreach ($votes as $v) {
$tally[$v] = ($tally[$v] ?? 0) + 1;
}
🎯 Quick Quiz
Question 1: You need a collection where every value is guaranteed unique and you'll frequently ask "is X already here?". Which structure fits best?
Question 2: Which single PHP type serves as both an ordered list and a keyed map?
Question 3: In Python, what is the idiomatic equivalent of JavaScript's nums.map(x => x * x)?
Summary & Quiz
🎉 Key Takeaways
- Almost every collection is one of four shapes: list, map, set, tuple.
- JS keeps Array / Object / Map / Set distinct; Python has list / dict / set / tuple; PHP folds list and map into one array type.
- map / filter / reduce (JS), comprehensions (Python), and array_map / array_filter / array_reduce (PHP) all express the same three transforms.
- Watch the gotchas: JS uses
.at(-1)for the last item, PHP'sarray_filterkeeps keys, and only Python has true immutable tuples. - Pick the structure by asking: keyed? unique? fixed-position?
📚 Further Reading
🚀 What's Next?
Now that you can move data between structures fluently, the next lesson tackles what happens when things go wrong — error handling in Node, Python & PHP, comparing try/catch, try/except, and Throwable side by side.
🎉 Nice work!
You can now read collections in three languages at a glance. Let's make your code resilient next.