Skip to main content

Arrays, Strings and Hash Maps in JavaScript

These three structures cover the majority of day-to-day code and interview questions. The key skill is knowing what each built-in method costs — the pleasant syntax hides very different price tags.

Arrays

JavaScript arrays are ordered, indexable, and dynamic. What each common operation costs:

OperationComplexityWhy
arr[i] read/writeO(1)Direct index access
push / popO(1) amortizedWorks at the end, nothing shifts
shift / unshiftO(n)Every remaining element is re-indexed
includes / indexOf / findO(n)Linear scan
sliceO(k)Copies k elements
spliceO(n)Shifts everything after the edit point
concat / spread [...a, ...b]O(n + m)Copies both inputs
map / filter / reduceO(n)One full pass plus the callback cost
sortO(n log n)Comparison sort

The two traps worth internalizing:

Trap 1 — shift in a loop. Draining an array from the front re-indexes the whole array every time, making the loop O(n^2):

const tasks = ["deploy", "review", "standup", "triage"];

while (tasks.length > 0) {
const task = tasks.shift(); // O(n) each time
console.log(task);
}

For hot paths, read with an index instead — same order, O(n) total:

const queue = ["deploy", "review", "standup", "triage"];
let head = 0;

while (head < queue.length) {
const task = queue[head++];
console.log(task);
}

Trap 2 — includes inside a loop. A linear scan inside a linear loop is O(n^2). See the Set section below for the O(n) fix.

Strings

Strings are immutable: every "modification" creates a new string.

OperationComplexityNotes
s[i] / charAt(i) / lengthO(1)
s + t concatenationO(n + m)Allocates a new string
slice / substringO(k)
includes / indexOfO(n)Substring scan
split / joinO(n)The standard bridge to array tools

Because strings are immutable, building one character by character with += in a loop can degrade badly. The idiomatic pattern is to collect pieces in an array and join once:

function initials(names) {
const parts = [];
for (const name of names) {
parts.push(name[0]);
}
return parts.join(".");
}

console.log(initials(["Rizwan", "Ashiq"])); // "R.A"

And the classic reverse, using the split–reverse–join bridge:

function reverse(s) {
return s.split("").reverse().join("");
}

console.log(reverse("Hafsa")); // "asfaH"

Hash maps: Map, Set, and plain objects

Hash-based structures are the workhorses of fast code: get, set, has, and delete are all O(1) on average.

Map vs plain object

AspectMapPlain object
Key typesAnything, including objectsStrings and symbols only — numbers coerce
Sizemap.sizeObject.keys(obj).length, an O(n) call
IterationInsertion order, directly iterableNeeds Object.keys / entries
Prototype baggageNoneInherits keys like toString

Use a Map when keys are dynamic data (user IDs, words, coordinates). Plain objects are fine as fixed-shape records.

Counting with a Map

The frequency-counter shape — count in one pass, answer in the next — solves an entire family of problems:

function mostFrequent(names) {
const counts = new Map();
for (const name of names) {
counts.set(name, (counts.get(name) || 0) + 1);
}

let best = null;
let bestCount = 0;
for (const [name, count] of counts) {
if (count > bestCount) {
best = name;
bestCount = count;
}
}
return best;
}

const attendees = ["Ayesha", "Ibrahim", "Ayesha", "Zakariya", "Ayesha", "Ibrahim"];
console.log(mostFrequent(attendees)); // "Ayesha"

O(n) time, O(k) space for k distinct keys.

Membership with a Set

The single most useful refactor in JavaScript performance work — replacing a scan-in-a-loop with a Set:

const registered = ["Rizwan", "Hafsa", "Ibrahim", "Ayesha"];
const checkedIn = ["Hafsa", "Zakariya", "Rizwan"];

// O(n * m): registered.includes runs a full scan per check-in
const slowMatches = checkedIn.filter((name) => registered.includes(name));

// O(n + m): build the Set once, each lookup is O(1)
const registeredSet = new Set(registered);
const fastMatches = checkedIn.filter((name) => registeredSet.has(name));

console.log(fastMatches); // ["Hafsa", "Rizwan"]

At 10,000 names each, that is the difference between 100 million comparisons and about 20,000 operations.

Deduplication is a one-liner for the same reason:

const visits = ["Rizwan", "Hafsa", "Rizwan", "Ayesha", "Hafsa"];
const uniqueVisitors = [...new Set(visits)];
console.log(uniqueVisitors); // ["Rizwan", "Hafsa", "Ayesha"]

Choosing between them

  • Need order and index access → array.
  • Need fast lookup, counting, or "have I seen this" → Map / Set.
  • Text processing → string methods, bridging to arrays via split / join when you need array tools.
  • Repeatedly inserting or removing at the front → none of the above; see Stacks, Queues and Linked Lists.

Practice problems for this page: the Array and String sections of the practice catalog.