DAY 1 / 30 · WEEK 1
JS Essentials for DSA
Goal of the day
- Read and write arrays and objects fast, without hesitating over syntax.
- Use destructuring and the spread operator confidently — they show up in almost every interview solution.
- Know which array operations are cheap (O(1)) and which are expensive (O(n)), before you even get to Big-O on Day 2.
Concept
Think of an array like a row of numbered lockers at a gym. Each locker has a fixed
position (its index), so you can open any locker instantly if you know
its number — that's why arr[2] is instant (O(1)).
The four basic ways to add/remove elements all come down to one question: does anything else have to move?
push(x)— addxat the end. Nothing else moves. O(1).pop()— remove the last element. Nothing else moves. O(1).unshift(x)— addxat the front. Every other element has to shift one locker to the right first, to make room. O(n).shift()— remove the first element. Every remaining element has to shift one locker to the left, to close the gap. O(n).
Destructuring is a shortcut for pulling values out of an array into named
variables in one line, instead of writing arr[0], arr[1]
separately — that part really is free, it's just reading. Spread
(...) goes the other way — it builds a whole new array by copying every value
out of the old one. That copy is still real work: O(n), same cost as writing the loop
yourself. Spread doesn't add a new capability, it's just a cleaner way to write a copy loop
you could've written by hand.
Diagrams
Our running example throughout this lesson
is a 5-element array: arr = [4, 7, 2, 9, 5] — big enough to actually see which
elements move and which don't.
Interactive visualizations
1. push, pop, unshift, shift — where does the cost come from?
2. Destructuring and spread
The code
// arr is a normal JS array — a contiguous, indexed list.
const arr = [4, 7, 2, 9, 5];
// push/pop touch only the END. Nothing else has to move. O(1).
arr.push(6); // arr is now [4, 7, 2, 9, 5, 6]
arr.pop(); // arr is back to [4, 7, 2, 9, 5]
// unshift/shift touch the START. Every other element has to
// shift over to make room (or close the gap). O(n).
arr.unshift(1); // arr is now [1, 4, 7, 2, 9, 5] — everyone else shifted right
arr.shift(); // arr is back to [4, 7, 2, 9, 5] — everyone else shifted left
// Reading by index is O(1): JS jumps straight to that memory slot.
const first = arr[0]; // 4
// Destructuring unpacks values into named variables in one line.
// It's identical to writing arr[0], arr[1], arr[2], arr[3], arr[4] separately.
const [a, b, c, d, e] = arr; // a=4, b=7, c=2, d=9, e=5
// Spread (...) copies every element of arr into a NEW array,
// then we add 6 at the end. arr itself is never changed.
const merged = [...arr, 6]; // [4, 7, 2, 9, 5, 6]
Line by line:
arr.push(6)— writes to the next free slot at the end. O(1).arr.pop()— deletes the last slot. O(1). Returns the removed value.arr.unshift(1)— inserts at index 0, but first shifts 4, 7, 2, 9, 5 each one slot to the right. O(n) because every element moves.arr.shift()— removes index 0, then shifts every remaining element one slot to the left to close the gap. Also O(n).arr[0]— direct index access. Always O(1), no matter how big the array is.const [a, b, c, d, e] = arr— destructuring. The pattern on the left mirrors the shape of the array on the right, position by position.[...arr, 6]— the spread operator expandsarr's elements in place, so this is really[4, 7, 2, 9, 5, 6]. It builds a new array.
Dry run
| Step | Code | arr | a, b, c, d, e | merged |
|---|---|---|---|---|
| 1 | const arr = [4,7,2,9,5] | [4,7,2,9,5] | — | — |
| 2 | arr.push(6) | [4,7,2,9,5,6] | — | — |
| 3 | arr.pop() | [4,7,2,9,5] | — | — |
| 4 | arr.unshift(1) | [1,4,7,2,9,5] | — | — |
| 5 | arr.shift() | [4,7,2,9,5] | — | — |
| 6 | const [a,b,c,d,e] = arr | [4,7,2,9,5] | 4, 7, 2, 9, 5 | — |
| 7 | const merged = [...arr, 6] | [4,7,2,9,5] | 4, 7, 2, 9, 5 | [4,7,2,9,5,6] |
Complexity
| Operation | Time | Space | Why |
|---|---|---|---|
Read by index arr[i] | O(1) | O(1) | Contiguous memory — index math jumps straight there. |
| Destructure n values* | O(n) | O(1) extra | Has to read n slots, one per variable. |
Spread copy [...arr] | O(n) | O(n) | Every element must be copied into the new array. |
push / pop (end) | O(1)† | O(1) | No shifting — just add/remove the last slot. |
unshift / shift (start) | O(n) | O(1) | Every remaining element has to shift one slot over. |
*Here n is the number of variables you're
destructuring, not the array's length — const [a, b] = hugeArray is O(2), not
O(hugeArray.length). †Amortized — occasionally the engine has to grow the underlying array,
but on average it's O(1).
Interview corner
- What's the difference between rest and spread syntax? Same
...symbol, opposite direction: spread expands an array/object into individual elements ([...arr]); rest collects individual elements into an array (function f(...args)).
↳ Common follow-up: "Can you use rest and spread in the same line?" Yes —const [first, ...rest] = arruses destructuring + rest together;fn(a, ...middle, b)for spread inside a call is also valid as long as there's only one rest element and it's last in a parameter list. - Does destructuring work on objects too? Yes —
const {a, b} = objmatches by key name instead of position. You can rename withconst {a: renamed} = obj, and set a default withconst {a = 5} = obj. - Is
[...arr]a deep copy or a shallow copy? Shallow. Ifarrcontains objects or nested arrays, the copy shares references to those same inner objects — mutating a nested object still affects both arrays.
⚠ Trap: candidates often say "spread makes a copy" and stop there. An interviewer will immediately follow up with "does that protect nested data?" — if you can't say "no, only the top level" you'll lose points even though your first answer was technically correct. - Why is
unshiftslower thanpushon a big array?pushjust writes to the next free slot.unshifthas to shift every existing element one position to the right first, which is O(n).
↳ Common follow-up: "So how would you build a queue efficiently in JS, given shift() is O(n)?" — good answer: use two pointers into an array (or a linked list, which we cover on Day 15) instead of repeatedly calling shift(). - What happens if you destructure more variables than the array has?
The extra variables are
undefined— no error is thrown.
⚠ Trap: this means a typo'd or off-by-one destructure fails silently instead of crashing — always sanity check array length first if the size isn't guaranteed.
How to talk through it out loud: say the plan before you type: "I'll destructure the first two values since I only need those, then use spread to build a new array without mutating the input — that keeps this O(n) instead of accidentally O(n²) from copying in a loop." Naming the shallow-copy gotcha unprompted is a strong signal to an interviewer that you've been burned by it before.
Practice problems
All three below are warm-ups (Easy) — Day 1 is about getting comfortable with the syntax. Real easy → medium pattern problems start from Day 3 onward, once we've built up more tools.
1. Concatenation of Array (Easy — LeetCode: Concatenation of Array)
Given an array nums, return an array ans of length
2n where ans is nums concatenated with itself.
function getConcatenation(nums) {
return [...nums, ...nums];
}2. Running Sum of 1d Array (Easy — LeetCode: Running Sum of 1d Array)
Given an array nums, return a new array where each element is the sum of
itself and every element before it.
result[i] and result[i - 1]? You shouldn't need to re-add
everything from scratch on every step.function runningSum(nums) {
const result = [];
let total = 0;
// .entries() yields [index, value] pairs — a natural fit for destructuring.
for (const [i, n] of nums.entries()) {
total += n;
result[i] = total;
}
return result;
}3. Shuffle the Array (Easy — LeetCode: Shuffle the Array)
Given nums = [x1,...,xn, y1,...,yn], return the array in the shuffled form
[x1,y1,x2,y2,...,xn,yn].
function shuffle(nums, n) {
const result = [];
for (let i = 0; i < n; i++) {
result.push(nums[i], nums[i + n]);
}
return result;
}Quiz
1. What does const [a, , c] = [1, 2, 3] set c to?
2. What is [...[1, 2], ...[3, 4]]?
3. Is [...arr] a shallow copy or a deep copy?
4. Which one runs in O(1) time: push or unshift?
5. What is a after const {a = 5} = {}?