4.4: Arrays & Objects

Learn how to work with arrays and objects to store and manipulate complex data in JavaScript. Master essential array methods, object properties, and techniques for accessing and modifying structured data.

1. Arrays

Creating Arrays

JavaScript:

// Array literal (most common)
const fruits = ["apple", "banana", "orange"];

// Array constructor (rarely used)
const numbers = new Array(1, 2, 3, 4, 5);

// Empty array
const empty = [];

// Mixed types (possible but not recommended)
const mixed = [1, "two", true, null, { name: "Alice" }];

Python comparison:

# Python list
fruits = ["apple", "banana", "orange"]
numbers = [1, 2, 3, 4, 5]
empty = []
mixed = [1, "two", True, None, {"name": "Alice"}]

Accessing Elements

const fruits = ["apple", "banana", "orange"];

// By index (0-based)
console.log(fruits[0]);   // "apple"
console.log(fruits[1]);   // "banana"
console.log(fruits[2]);   // "orange"

// Negative indices NOT supported (unlike Python)
console.log(fruits[-1]);  // undefined (not "orange")

// Get last element
console.log(fruits[fruits.length - 1]);  // "orange"

// Array length
console.log(fruits.length);  // 3

Python comparison:

fruits = ["apple", "banana", "orange"]
fruits[0]    # "apple"
fruits[-1]   # "orange" (negative indexing works!)
len(fruits)  # 3

Modifying Arrays

Arrays are mutable:

const fruits = ["apple", "banana"];

// Add to end
fruits.push("orange");        // ["apple", "banana", "orange"]

// Remove from end
const last = fruits.pop();    // "orange", fruits = ["apple", "banana"]

// Add to beginning
fruits.unshift("mango");      // ["mango", "apple", "banana"]

// Remove from beginning
const first = fruits.shift(); // "mango", fruits = ["apple", "banana"]

// Modify by index
fruits[0] = "grape";          // ["grape", "banana"]

Python comparison:

fruits = ["apple", "banana"]

fruits.append("orange")    # Add to end
fruits.pop()               # Remove from end
fruits.insert(0, "mango")  # Add to beginning
fruits.pop(0)              # Remove from beginning
fruits[0] = "grape"        # Modify by index

2. Array Methods

map() - Transform Each Element

Creates a new array by transforming each element:

const numbers = [1, 2, 3, 4, 5];

// Double each number
const doubled = numbers.map(x => x * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// Extract property from objects
const users = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 }
];
const names = users.map(user => user.name);
console.log(names);  // ["Alice", "Bob"]

Python comparison:

numbers = [1, 2, 3, 4, 5]

# Using map
doubled = list(map(lambda x: x * 2, numbers))

# List comprehension (more Pythonic)
doubled = [x * 2 for x in numbers]

filter() - Select Elements

Creates a new array with elements that pass a test:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Get even numbers
const evens = numbers.filter(x => x % 2 === 0);
console.log(evens);  // [2, 4, 6, 8, 10]

// Filter objects
const users = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 17 },
  { name: "Charlie", age: 25 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults);  // [{ name: "Alice", age: 30 }, { name: "Charlie", age: 25 }]

Python comparison:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Using filter
evens = list(filter(lambda x: x % 2 == 0, numbers))

# List comprehension
evens = [x for x in numbers if x % 2 == 0]

reduce() - Combine to Single Value

Reduces array to a single value:

const numbers = [1, 2, 3, 4, 5];

// Sum all numbers
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum);  // 15

// Find max
const max = numbers.reduce((max, num) => num > max ? num : max, numbers[0]);
console.log(max);  // 5

// Build object from array
const users = ["Alice", "Bob", "Charlie"];
const userObj = users.reduce((obj, name, index) => {
  obj[index] = name;
  return obj;
}, {});
console.log(userObj);  // { 0: "Alice", 1: "Bob", 2: "Charlie" }

Python comparison:

from functools import reduce

numbers = [1, 2, 3, 4, 5]

# Sum
total = reduce(lambda a, b: a + b, numbers, 0)

# Or built-in sum
total = sum(numbers)

find() and findIndex()

const numbers = [1, 2, 3, 4, 5];

// Find first element > 3
const found = numbers.find(x => x > 3);
console.log(found);  // 4

// Find index of first element > 3
const index = numbers.findIndex(x => x > 3);
console.log(index);  // 3

// Find object
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
];
const user = users.find(u => u.id === 2);
console.log(user);  // { id: 2, name: "Bob" }

some() and every()

const numbers = [1, 2, 3, 4, 5];

// Check if ANY element is even
const hasEven = numbers.some(x => x % 2 === 0);
console.log(hasEven);  // true

// Check if ALL elements are positive
const allPositive = numbers.every(x => x > 0);
console.log(allPositive);  // true

Python comparison:

numbers = [1, 2, 3, 4, 5]

# some
has_even = any(x % 2 == 0 for x in numbers)  # True

# every
all_positive = all(x > 0 for x in numbers)   # True

includes() and indexOf()

const fruits = ["apple", "banana", "orange"];

// Check if array contains element
console.log(fruits.includes("banana"));  // true
console.log(fruits.includes("grape"));   // false

// Find index
console.log(fruits.indexOf("banana"));   // 1
console.log(fruits.indexOf("grape"));    // -1 (not found)

Python comparison:

fruits = ["apple", "banana", "orange"]

"banana" in fruits     # True
fruits.index("banana") # 1
# fruits.index("grape")  # Error: not in list

Slicing and Splicing

slice() - Extract portion (doesn't modify original):

const numbers = [1, 2, 3, 4, 5];

const sliced = numbers.slice(1, 3);  // [2, 3]
const fromIndex = numbers.slice(2);  // [3, 4, 5]
const lastTwo = numbers.slice(-2);   // [4, 5]

console.log(numbers);  // [1, 2, 3, 4, 5] (unchanged)

splice() - Modify original array:

const numbers = [1, 2, 3, 4, 5];

// Remove 2 elements starting at index 1
const removed = numbers.splice(1, 2);
console.log(removed);  // [2, 3]
console.log(numbers);  // [1, 4, 5]

// Insert elements
numbers.splice(1, 0, "a", "b");  // Insert at index 1
console.log(numbers);  // [1, "a", "b", 4, 5]

// Replace elements
numbers.splice(1, 2, "x");  // Remove 2, insert "x"
console.log(numbers);  // [1, "x", 4, 5]

Python comparison:

numbers = [1, 2, 3, 4, 5]

# Slicing (doesn't modify)
sliced = numbers[1:3]   # [2, 3]
from_index = numbers[2:]  # [3, 4, 5]
last_two = numbers[-2:]   # [4, 5]

# Modifying requires del, insert, etc.
del numbers[1:3]  # Remove elements

Other Useful Methods

const numbers = [3, 1, 4, 1, 5, 9, 2, 6];

// Sort (mutates original!)
numbers.sort();
console.log(numbers);  // [1, 1, 2, 3, 4, 5, 6, 9]

// Sort with custom comparator
numbers.sort((a, b) => b - a);  // Descending
console.log(numbers);  // [9, 6, 5, 4, 3, 2, 1, 1]

// Reverse (mutates original!)
numbers.reverse();
console.log(numbers);  // [1, 1, 2, 3, 4, 5, 6, 9]

// Join to string
const text = numbers.join(", ");
console.log(text);  // "1, 1, 2, 3, 4, 5, 6, 9"

// Flat (flatten nested arrays)
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat());     // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2));    // [1, 2, 3, 4, 5, 6]

3. Array Destructuring

Extract values from arrays:

const colors = ["red", "green", "blue"];

// Old way
const first = colors[0];
const second = colors[1];

// Destructuring
const [r, g, b] = colors;
console.log(r);  // "red"
console.log(g);  // "green"
console.log(b);  // "blue"

// Skip elements
const [, , third] = colors;
console.log(third);  // "blue"

// Rest operator
const [primary, ...others] = colors;
console.log(primary);  // "red"
console.log(others);   // ["green", "blue"]

// Swap variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b);  // 2, 1

Python comparison:

colors = ["red", "green", "blue"]

# Unpacking
r, g, b = colors

# Skip elements
_, _, third = colors

# Rest (Python 3.0+)
primary, *others = colors  # primary = "red", others = ["green", "blue"]

# Swap
a, b = 1, 2
a, b = b, a

4. Objects

Creating Objects

// Object literal (most common)
const user = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// Empty object
const empty = {};

// Computed property names
const key = "dynamic";
const obj = {
  [key]: "value",
  [`${key}Key`]: "another value"
};
console.log(obj);  // { dynamic: "value", dynamicKey: "another value" }

Python comparison:

# Python dictionary
user = {
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com"
}

empty = {}

Accessing Properties

const user = {
  name: "Alice",
  age: 30,
  "favorite-color": "blue"
};

// Dot notation
console.log(user.name);  // "Alice"
console.log(user.age);   // 30

// Bracket notation (for dynamic keys or special characters)
console.log(user["name"]);             // "Alice"
console.log(user["favorite-color"]);   // "blue"

const key = "age";
console.log(user[key]);  // 30

Python:

user = {
    "name": "Alice",
    "age": 30,
    "favorite-color": "blue"
}

user["name"]           # "Alice"
user.get("name")       # "Alice" (safer)
user["favorite-color"] # "blue"

key = "age"
user[key]              # 30

Modifying Objects

const user = {
  name: "Alice",
  age: 30
};

// Add property
user.email = "alice@example.com";
user["phone"] = "555-1234";

// Modify property
user.age = 31;

// Delete property
delete user.phone;

console.log(user);
// { name: "Alice", age: 31, email: "alice@example.com" }

Object Methods

const user = {
  name: "Alice",
  age: 30,
  greet() {
    return `Hello, I'm ${this.name}`;
  },
  // Arrow function (careful with 'this'!)
  sayAge: () => {
    // return `I'm ${this.age}`;  // Won't work! Arrow has no 'this'
  }
};

console.log(user.greet());  // "Hello, I'm Alice"

Python comparison:

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, I'm {self.name}"

user = User("Alice", 30)
user.greet()  # "Hello, I'm Alice"

Object Iteration

const user = {
  name: "Alice",
  age: 30,
  city: "NYC"
};

// Get keys
const keys = Object.keys(user);
console.log(keys);  // ["name", "age", "city"]

// Get values
const values = Object.values(user);
console.log(values);  // ["Alice", 30, "NYC"]

// Get entries (key-value pairs)
const entries = Object.entries(user);
console.log(entries);
// [["name", "Alice"], ["age", 30], ["city", "NYC"]]

// Iterate
for (const key in user) {
  console.log(key, user[key]);
}

// Better: Object.entries()
for (const [key, value] of Object.entries(user)) {
  console.log(key, value);
}

Python comparison:

user = {"name": "Alice", "age": 30, "city": "NYC"}

user.keys()        # dict_keys(["name", "age", "city"])
user.values()      # dict_values(["Alice", 30, "NYC"])
user.items()       # dict_items([("name", "Alice"), ...])

for key, value in user.items():
    print(key, value)

5. Object Destructuring

const user = {
  name: "Alice",
  age: 30,
  city: "NYC"
};

// Destructuring
const { name, age } = user;
console.log(name);  // "Alice"
console.log(age);   // 30

// Rename variables
const { name: userName, age: userAge } = user;
console.log(userName);  // "Alice"

// Default values
const { country = "USA" } = user;
console.log(country);  // "USA"

// Rest operator
const { name: n, ...rest } = user;
console.log(n);     // "Alice"
console.log(rest);  // { age: 30, city: "NYC" }

// Nested destructuring
const data = {
  user: {
    name: "Alice",
    address: {
      city: "NYC"
    }
  }
};

const { user: { name: userName2, address: { city } } } = data;
console.log(userName2);  // "Alice"
console.log(city);       // "NYC"

Function parameter destructuring:

function greet({ name, age }) {
  return `Hello ${name}, you are ${age} years old`;
}

const user = { name: "Alice", age: 30, city: "NYC" };
console.log(greet(user));  // "Hello Alice, you are 30 years old"

6. Spread and Rest with Objects

Spread Operator

Copy and merge objects:

const user = { name: "Alice", age: 30 };

// Shallow copy
const copy = { ...user };

// Merge objects
const address = { city: "NYC", country: "USA" };
const fullUser = { ...user, ...address };
console.log(fullUser);
// { name: "Alice", age: 30, city: "NYC", country: "USA" }

// Override properties
const updated = { ...user, age: 31 };
console.log(updated);  // { name: "Alice", age: 31 }

Python comparison:

user = {"name": "Alice", "age": 30}

# Copy (shallow)
copy = user.copy()
# Or
copy = {**user}

# Merge
address = {"city": "NYC", "country": "USA"}
full_user = {**user, **address}

# Override
updated = {**user, "age": 31}

Rest Operator

function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4));  // 10

// With destructuring
const { name, ...otherProps } = { name: "Alice", age: 30, city: "NYC" };
console.log(name);        // "Alice"
console.log(otherProps);  // { age: 30, city: "NYC" }

7. JSON (JavaScript Object Notation)

JSON.stringify() - Object to String

const user = {
  name: "Alice",
  age: 30,
  active: true
};

const json = JSON.stringify(user);
console.log(json);  // '{"name":"Alice","age":30,"active":true}'

// Pretty print
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
/*
{
  "name": "Alice",
  "age": 30,
  "active": true
}
*/

Python comparison:

import json

user = {"name": "Alice", "age": 30, "active": True}

# To JSON string
json_str = json.dumps(user)

# Pretty print
pretty = json.dumps(user, indent=2)

JSON.parse() - String to Object

const jsonString = '{"name":"Alice","age":30}';
const user = JSON.parse(jsonString);

console.log(user.name);  // "Alice"
console.log(user.age);   // 30

Python:

import json

json_str = '{"name":"Alice","age":30}'
user = json.loads(json_str)

user["name"]  # "Alice"

8. Practical Examples

Example 1: Data Transformation

const users = [
  { id: 1, name: "Alice", age: 30, active: true },
  { id: 2, name: "Bob", age: 25, active: false },
  { id: 3, name: "Charlie", age: 35, active: true }
];

// Get active users' names
const activeNames = users
  .filter(u => u.active)
  .map(u => u.name);
console.log(activeNames);  // ["Alice", "Charlie"]

// Average age
const avgAge = users.reduce((sum, u) => sum + u.age, 0) / users.length;
console.log(avgAge);  // 30

Example 2: Group By

const products = [
  { name: "Apple", category: "Fruit" },
  { name: "Carrot", category: "Vegetable" },
  { name: "Banana", category: "Fruit" },
  { name: "Broccoli", category: "Vegetable" }
];

const grouped = products.reduce((acc, item) => {
  const { category } = item;
  if (!acc[category]) {
    acc[category] = [];
  }
  acc[category].push(item);
  return acc;
}, {});

console.log(grouped);
/*
{
  Fruit: [{ name: "Apple", ... }, { name: "Banana", ... }],
  Vegetable: [{ name: "Carrot", ... }, { name: "Broccoli", ... }]
}
*/

Example 3: Deep Clone

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

const original = {
  name: "Alice",
  address: { city: "NYC" }
};

const clone = deepClone(original);
clone.address.city = "LA";

console.log(original.address.city);  // "NYC" (unchanged)
console.log(clone.address.city);     // "LA"

9. Practical Exercises

Exercise 4.4.1: Array Methods Chain

Given an array of numbers, use method chaining to:

  1. Filter for numbers > 10
  2. Square each number
  3. Sum the results

Exercise 4.4.2: Object Manipulation

Create a function that:

  1. Takes an array of user objects
  2. Returns an object keyed by user ID
  3. Each value contains only name and email

Exercise 4.4.3: Destructuring Practice

Rewrite using destructuring:

function getFullName(user) {
  return user.firstName + " " + user.lastName;
}

Exercise 4.4.4: Data Processing

Process this data to get unique categories:

const items = [
  { name: "Item 1", category: "A" },
  { name: "Item 2", category: "B" },
  { name: "Item 3", category: "A" }
];

10. Knowledge Check

Question 1: What's the difference between map() and forEach()?

Show answer map() returns a new array with transformed elements. forEach() just iterates and returns undefined. Use map() for transformations, forEach() for side effects.

Question 2: When should you use bracket notation vs dot notation?

Show answer Use bracket notation for: dynamic keys (variables), special characters in keys, or property names that are reserved words. Otherwise use dot notation.

Question 3: What does the spread operator do with objects?

Show answer Spreads object properties into a new object. Creates shallow copy and allows merging: `{...obj1, ...obj2}`.

Question 4: What's the difference between slice() and splice()?

Show answer slice() extracts portion without modifying original. splice() modifies original array by removing/inserting elements.

11. Key Takeaways

  • Arrays are ordered, zero-indexed collections
  • Use map(), filter(), reduce() for transformations
  • find(), some(), every() for searching
  • slice() extracts, splice() modifies
  • Objects store key-value pairs
  • Access properties with dot (obj.key) or bracket (objkey)
  • Object.keys/values/entries() for iteration
  • Destructuring extracts values: const {name} = obj
  • Spread copies/merges: {...obj} or [...arr]
  • JSON.stringify() → string, JSON.parse() → object

12. Further Resources

Documentation:


Next Steps

Excellent work! You now master JavaScript's core data structures.

In Lesson 4.5: DOM Manipulation, you'll learn to interact with HTML elements using JavaScript to create dynamic web pages.

Next: Lesson 4.5 - DOM Manipulation →