Objects & Hash Maps in JavaScript - The Power of Key-Value Pairs! 🗂️

Author
Code Index
Published on
November 25, 2024
Reading time
16 min read
Category
Dsa
Objects & Hash Maps in JavaScript - The Power of Key-Value Pairs! 🗂️

Objects & Hash Maps - The Power of Key-Value Pairs! 🗂️

A Story to Start With...

Imagine you have a magic notebook where you can write down information about your friends:

Sarah's age: 10
Sarah's favorite color: Blue
Sarah's favorite food: Pizza

Mike's age: 11
Mike's favorite color: Red
Mike's favorite food: Burgers

Instead of remembering positions (like arrays), you remember things by names (keys)!

This is exactly what objects do! 🎯

What is an Object? (The Super Simple Version)

An object is like a labeled filing cabinet:

const sarah = {
  age: 10,
  favoriteColor: "blue",
  favoriteFood: "pizza",
};

// Instead of: sarah[0], sarah[1], sarah[2] (confusing!)
// We use: sarah.age, sarah.favoriteColor, sarah.favoriteFood (clear!)

Key-Value Pairs:

  • Key = The label (like "age")
  • Value = The information (like 10)

Creating Objects (Different Ways!)

Way 1: Object Literal (Most Common!)

const person = {
  name: "Sarah",
  age: 10,
  city: "New York",
};

console.log(person);
// { name: "Sarah", age: 10, city: "New York" }

Way 2: Empty Object (Add Properties Later)

const person = {};

// Add properties one by one
person.name = "Sarah";
person.age = 10;
person.city = "New York";

console.log(person);
// { name: "Sarah", age: 10, city: "New York" }

Way 3: Using new Object() (Less Common)

const person = new Object();
person.name = "Sarah";
person.age = 10;

console.log(person);
// { name: "Sarah", age: 10 }

Way 4: With Variables as Keys!

const keyName = "favoriteColor";
const value = "blue";

const person = {
  name: "Sarah",
  [keyName]: value, // Use variable as key!
};

console.log(person);
// { name: "Sarah", favoriteColor: "blue" }

Accessing Object Properties (Getting Values!)

Method 1: Dot Notation (Easy to Read!)

const person = {
  name: "Sarah",
  age: 10,
  city: "New York",
};

console.log(person.name); // "Sarah"
console.log(person.age); // 10
console.log(person.city); // "New York"

Method 2: Bracket Notation (More Flexible!)

const person = {
  name: "Sarah",
  age: 10,
  "favorite color": "blue", // Key with space!
};

console.log(person["name"]); // "Sarah"
console.log(person["favorite color"]); // "blue" (can't use dot notation here!)

// Use variables!
const key = "age";
console.log(person[key]); // 10

When to use which?

  • Dot notation: When key is a simple word
  • Bracket notation: When key has spaces, special characters, or is a variable

Adding & Modifying Properties

const person = {
  name: "Sarah",
  age: 10,
};

// Add new property
person.city = "New York";
person["favorite color"] = "blue";

// Modify existing property
person.age = 11;

console.log(person);
// {
//   name: "Sarah",
//   age: 11,
//   city: "New York",
//   "favorite color": "blue"
// }

Deleting Properties

const person = {
  name: "Sarah",
  age: 10,
  city: "New York",
};

// Delete a property
delete person.city;

console.log(person);
// { name: "Sarah", age: 10 }

Checking if Property Exists

const person = {
  name: "Sarah",
  age: 10,
};

// Method 1: Using 'in' operator
console.log("name" in person); // true
console.log("city" in person); // false

// Method 2: Using hasOwnProperty
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("city")); // false

// Method 3: Check if undefined
console.log(person.name !== undefined); // true
console.log(person.city !== undefined); // false

Essential Object Methods! 🛠️

1. Object.keys() - Get All Keys

const person = {
  name: "Sarah",
  age: 10,
  city: "New York",
};

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

// Count how many properties
console.log(keys.length); // 3

Real-life example: List all settings!

const settings = {
  darkMode: true,
  fontSize: 16,
  language: "English",
};

console.log("Available settings:");
Object.keys(settings).forEach((setting) => {
  console.log(`- ${setting}`);
});
// Available settings:
// - darkMode
// - fontSize
// - language

2. Object.values() - Get All Values

const scores = {
  math: 95,
  english: 87,
  science: 92,
};

const allScores = Object.values(scores);
console.log(allScores); // [95, 87, 92]

// Calculate average
const average =
  allScores.reduce((sum, score) => sum + score, 0) / allScores.length;
console.log(average); // 91.33

3. Object.entries() - Get Key-Value Pairs

const person = {
  name: "Sarah",
  age: 10,
  city: "New York",
};

const entries = Object.entries(person);
console.log(entries);
// [
//   ["name", "Sarah"],
//   ["age", 10],
//   ["city", "New York"]
// ]

// Loop through all properties
entries.forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});
// name: Sarah
// age: 10
// city: New York

4. Object.assign() - Copy/Merge Objects

const person = { name: "Sarah", age: 10 };
const address = { city: "New York", country: "USA" };

// Merge objects
const fullInfo = Object.assign({}, person, address);
console.log(fullInfo);
// { name: "Sarah", age: 10, city: "New York", country: "USA" }

// Modern way using spread operator
const fullInfo2 = { ...person, ...address };
console.log(fullInfo2);
// { name: "Sarah", age: 10, city: "New York", country: "USA" }

5. Object.freeze() - Make Object Unchangeable

const settings = {
  maxUsers: 100,
  timeout: 30,
};

Object.freeze(settings);

// Try to change (won't work!)
settings.maxUsers = 200;
settings.newSetting = "test";

console.log(settings);
// { maxUsers: 100, timeout: 30 } (unchanged!)

Objects vs Arrays - When to Use What?

Use Arrays When:

  • ✅ Order matters
  • ✅ You need to loop through items
  • ✅ Items are similar (list of names, list of numbers)
const fruits = ["apple", "banana", "orange"];
const scores = [95, 87, 92, 88];

Use Objects When:

  • ✅ You need to look up by name
  • ✅ Properties have different meanings
  • ✅ You want descriptive keys
const person = {
  name: "Sarah",
  age: 10,
  favoriteColor: "blue",
};

const settings = {
  darkMode: true,
  fontSize: 16,
  language: "English",
};

Hash Maps - Objects on Steroids! 💪

JavaScript has a special type called Map that's like an object but with superpowers!

Creating a Map

// Create empty Map
const phoneBook = new Map();

// Add entries
phoneBook.set("Sarah", "555-0001");
phoneBook.set("Mike", "555-0002");
phoneBook.set("Emma", "555-0003");

console.log(phoneBook);
// Map(3) {
//   "Sarah" => "555-0001",
//   "Mike" => "555-0002",
//   "Emma" => "555-0003"
// }

Map Methods

const phoneBook = new Map();
phoneBook.set("Sarah", "555-0001");
phoneBook.set("Mike", "555-0002");

// Get value
console.log(phoneBook.get("Sarah")); // "555-0001"

// Check if exists
console.log(phoneBook.has("Sarah")); // true
console.log(phoneBook.has("Lisa")); // false

// Delete entry
phoneBook.delete("Mike");

// Get size
console.log(phoneBook.size); // 1

// Clear all
phoneBook.clear();
console.log(phoneBook.size); // 0

Map vs Object

FeatureObjectMap
KeysStrings/Symbols onlyAny type!
OrderNot guaranteedGuaranteed
SizeManual count.size property
IterationNeed Object.keys()Built-in iteration
PerformanceGoodBetter for frequent add/delete

When to use Map:

  • Need non-string keys
  • Need to know size easily
  • Frequent additions/deletions
  • Need guaranteed order
// Map can use ANY type as key!
const map = new Map();

map.set(1, "number key");
map.set(true, "boolean key");
map.set({ id: 1 }, "object key");

console.log(map.get(1)); // "number key"
console.log(map.get(true)); // "boolean key"

Common Object Patterns (Interview Favorites!)

Pattern 1: Frequency Counter

Problem: Count how many times each item appears!

function countFrequency(arr) {
  const frequency = {};

  for (let item of arr) {
    // If item exists, add 1, otherwise start at 1
    frequency[item] = (frequency[item] || 0) + 1;
  }

  return frequency;
}

const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
console.log(countFrequency(fruits));
// { apple: 3, banana: 2, orange: 1 }

// Using Map
function countFrequencyMap(arr) {
  const frequency = new Map();

  for (let item of arr) {
    frequency.set(item, (frequency.get(item) || 0) + 1);
  }

  return frequency;
}

Pattern 2: Group By Property

Problem: Group students by grade!

function groupByGrade(students) {
  const groups = {};

  for (let student of students) {
    const grade = student.grade;

    // Create array if doesn't exist
    if (!groups[grade]) {
      groups[grade] = [];
    }

    groups[grade].push(student.name);
  }

  return groups;
}

const students = [
  { name: "Sarah", grade: "A" },
  { name: "Mike", grade: "B" },
  { name: "Emma", grade: "A" },
  { name: "Alex", grade: "B" },
];

console.log(groupByGrade(students));
// {
//   A: ["Sarah", "Emma"],
//   B: ["Mike", "Alex"]
// }

Pattern 3: Two Sum Problem

Problem: Find two numbers that add up to target!

function twoSum(nums, target) {
  const seen = new Map();

  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];

    if (seen.has(complement)) {
      return [seen.get(complement), i];
    }

    seen.set(nums[i], i);
  }

  return null;
}

const numbers = [2, 7, 11, 15];
console.log(twoSum(numbers, 9)); // [0, 1] (2 + 7 = 9)

Pattern 4: Caching/Memoization

Problem: Remember previous calculations to speed up!

function fibonacci(n, cache = {}) {
  // Check cache first
  if (n in cache) {
    return cache[n];
  }

  // Base cases
  if (n <= 1) {
    return n;
  }

  // Calculate and store in cache
  cache[n] = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
  return cache[n];
}

console.log(fibonacci(10)); // 55 (super fast!)
console.log(fibonacci(50)); // Works instantly with cache!

Pattern 5: Object as Set

Problem: Remove duplicates using object!

function removeDuplicates(arr) {
  const seen = {};
  const result = [];

  for (let item of arr) {
    if (!seen[item]) {
      seen[item] = true;
      result.push(item);
    }
  }

  return result;
}

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

Practice Challenges! 🎮

Challenge 1: Invert Object

Swap keys and values!

function invertObject(obj) {
  // Your code here!
  // Hint: Loop through entries and swap!
}

const original = { a: 1, b: 2, c: 3 };
console.log(invertObject(original)); // { 1: "a", 2: "b", 3: "c" }
Click to see the answer!
function invertObject(obj) {
  const inverted = {};

  for (let [key, value] of Object.entries(obj)) {
    inverted[value] = key;
  }

  return inverted;
}

// Or using reduce:
function invertObject(obj) {
  return Object.entries(obj).reduce((acc, [key, value]) => {
    acc[value] = key;
    return acc;
  }, {});
}

Challenge 2: Merge Objects

Merge multiple objects into one!

function mergeObjects(...objects) {
  // Your code here!
  // Hint: Use Object.assign or spread operator!
}

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const obj3 = { e: 5 };

console.log(mergeObjects(obj1, obj2, obj3));
// { a: 1, b: 2, c: 3, d: 4, e: 5 }
Click to see the answer!
function mergeObjects(...objects) {
  return Object.assign({}, ...objects);
}

// Or using spread operator:
function mergeObjects(...objects) {
  return { ...objects.reduce((acc, obj) => ({ ...acc, ...obj }), {}) };
}

// Simplest way:
function mergeObjects(...objects) {
  let result = {};
  for (let obj of objects) {
    result = { ...result, ...obj };
  }
  return result;
}

Challenge 3: Find Most Frequent

Find the most frequent item in an array!

function mostFrequent(arr) {
  // Your code here!
  // Hint: Count frequency, then find max!
}

const items = ["apple", "banana", "apple", "orange", "banana", "apple"];
console.log(mostFrequent(items)); // "apple"
Click to see the answer!
function mostFrequent(arr) {
  const frequency = {};
  let maxCount = 0;
  let mostFrequentItem = null;

  // Count frequencies
  for (let item of arr) {
    frequency[item] = (frequency[item] || 0) + 1;

    // Track max while counting
    if (frequency[item] > maxCount) {
      maxCount = frequency[item];
      mostFrequentItem = item;
    }
  }

  return mostFrequentItem;
}

Challenge 4: Deep Clone Object

Create a complete copy of an object (including nested objects)!

function deepClone(obj) {
  // Your code here!
  // Hint: Recursion or JSON methods!
}

const original = {
  name: "Sarah",
  address: {
    city: "New York",
    country: "USA",
  },
};

const copy = deepClone(original);
copy.address.city = "Boston";

console.log(original.address.city); // Should still be "New York"
console.log(copy.address.city); // "Boston"
Click to see the answer!
// Method 1: Using JSON (simple but has limitations)
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

// Method 2: Recursive (more robust)
function deepClone(obj) {
  // Handle null and non-objects
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // Handle arrays
  if (Array.isArray(obj)) {
    return obj.map((item) => deepClone(item));
  }

  // Handle objects
  const cloned = {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }

  return cloned;
}

Challenge 5: Object Difference

Find properties that are different between two objects!

function objectDiff(obj1, obj2) {
  // Your code here!
  // Hint: Compare each property!
}

const person1 = { name: "Sarah", age: 10, city: "New York" };
const person2 = { name: "Sarah", age: 11, city: "Boston" };

console.log(objectDiff(person1, person2));
// { age: [10, 11], city: ["New York", "Boston"] }
Click to see the answer!
function objectDiff(obj1, obj2) {
  const diff = {};

  // Check all keys from both objects
  const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);

  for (let key of allKeys) {
    if (obj1[key] !== obj2[key]) {
      diff[key] = [obj1[key], obj2[key]];
    }
  }

  return diff;
}

Nested Objects - Objects Inside Objects!

const school = {
  name: "Sunny Elementary",
  students: {
    grade1: ["Alice", "Bob"],
    grade2: ["Charlie", "David"],
  },
  teachers: {
    math: "Mr. Smith",
    english: "Ms. Johnson",
  },
};

// Access nested properties
console.log(school.students.grade1); // ["Alice", "Bob"]
console.log(school.teachers.math); // "Mr. Smith"

// Add to nested array
school.students.grade1.push("Emma");
console.log(school.students.grade1); // ["Alice", "Bob", "Emma"]

Common Mistakes to Avoid! ⚠️

Mistake 1: Modifying Object While Looping

// ❌ Wrong
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  delete obj[key]; // Dangerous!
}

// ✅ Right
const obj = { a: 1, b: 2, c: 3 };
const keysToDelete = Object.keys(obj);
keysToDelete.forEach((key) => delete obj[key]);

Mistake 2: Comparing Objects

// ❌ Wrong - compares references, not values
const obj1 = { a: 1 };
const obj2 = { a: 1 };
console.log(obj1 === obj2); // false!

// ✅ Right - compare as JSON strings
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true

Mistake 3: Forgetting Objects are References

// ❌ Wrong - both variables point to same object
const original = { name: "Sarah" };
const copy = original;
copy.name = "Mike";
console.log(original.name); // "Mike" (changed!)

// ✅ Right - create new object
const original = { name: "Sarah" };
const copy = { ...original };
copy.name = "Mike";
console.log(original.name); // "Sarah" (unchanged!)

Performance Tips! ⚡

Tip 1: Use Map for Frequent Lookups

// ❌ Slower for many lookups
const obj = {
  /* millions of entries */
};
if ("key" in obj) {
}

// ✅ Faster
const map = new Map(/* millions of entries */);
if (map.has("key")) {
}

Tip 2: Cache Object.keys()

// ❌ Slow - creates new array each time
for (let i = 0; i < 1000; i++) {
  Object.keys(obj).forEach((key) => {});
}

// ✅ Fast - create once
const keys = Object.keys(obj);
for (let i = 0; i < 1000; i++) {
  keys.forEach((key) => {});
}

Key Takeaways! 🎯

  1. Objects store key-value pairs - Like a labeled filing cabinet
  2. Use objects for lookups - Fast access by key name
  3. Maps are more powerful - Any type as key, better performance
  4. Objects are references - Copying needs special care
  5. Many useful methods - Object.keys(), values(), entries()
  6. Perfect for counting - Frequency counters, grouping

Quick Reference Card 📋

// Creating
const obj = { key: "value" };
const map = new Map();

// Accessing
obj.key; // Dot notation
obj["key"]; // Bracket notation
map.get("key"); // Map get

// Adding/Modifying
obj.key = "new";
map.set("key", "new");

// Deleting
delete obj.key;
map.delete("key");

// Checking
"key" in obj;
obj.hasOwnProperty("key");
map.has("key");

// Methods
Object.keys(obj); // Get keys
Object.values(obj); // Get values
Object.entries(obj); // Get pairs
map.size; // Get size

// Looping
for (let key in obj) {
}
for (let [k, v] of map) {
}

What's Next?

In the next episode, we'll learn about Sets - a special data structure for unique values!

We'll cover:

  • What Sets are and how they work
  • Set methods and operations
  • When to use Sets vs Arrays
  • Common Set patterns

This is Episode 4 of the "Mastering DSA with JavaScript" series.

Previous Episode: Strings in JavaScript →

Next Episode: Sets in JavaScript - Unique Values Only! →

Questions? Drop a comment below! 💬