Level Up Your JavaScript: A Practical Guide to Array Manipulation

Arrays are one of the most important data structures in JavaScript. But knowing how to store data is only half the battle, but what to do with the array and data stored in it? How can the array be manipulated? This is where array methods come into play. These are the built in functions provided by the JavaScript.
Mutating vs Non-Mutating
The array methods are of two types, Mutating and Non-Mutating. They have certain behavior when called upon an array:
| Type | What it means | Methods |
|---|---|---|
| Mutating | Changes the original array directly | push, pop, shift, unshift |
| Non-Mutating | Returns a NEW array and the original is untouched | map, filter, reduce, forEach |
// Mutating = original array changes
const playlist = ["Starboy", "Blinding Lights"];
playlist.push("As It Was");
console.log(playlist); // ["Starboy", "Blinding Lights", "As It Was"]
// Original was changed
// Non-mutating = original array is safe
const scores = [10, 20, 30];
const doubled = scores.map(n => n * 2);
console.log(scores); // [10, 20, 30] | untouched
console.log(doubled); // [20, 40, 60] | brand new array
Understanding the Callback Function
map(), filter(), forEach(), and reduce() accept a callback which is a function you provide that gets called on each element. That callback receives up to 3 arguments:
array.map((element, index, array) => {
// โ โ โ
// the item position the full array
});
You only need to declare the ones you'll use:
const playlist = ["Starboy", "Blinding Lights", "Flowers"];
// Just the element
playlist.forEach(playlist => console.log(playlist));
// Element + index
playlist.forEach((playlist, index) => {
console.log(`\({index + 1}. \){playlist}`);
});
// 1. Starboy
// 2. Blinding Lights
// 3. Flowers
Array Methods
push() and pop()
push(): Add to the end.
push() adds one or more elements to the end of an array and returns the new length.
// BEFORE
const playlist = ["Starboy", "Blinding Lights"];
console.log(playlist); // ["Starboy", "Blinding Lights"]
const newLength = playlist.push("As It Was");
// AFTER
console.log(playlist); // ["Starboy", "Blinding Lights", "As It Was"]
console.log(newLength); // 3 โ returns new length, not the array itself
You can push multiple items at once:
const playlist = ["Starboy"];
playlist.push("Blinding Lights", "As It Was", "Lucid Dreams");
console.log(playlist);
// ["Starboy", "Blinding Lights", "As It Was", "Lucid Dreams"]
Real example: user builds their playlist:
const myPlaylist = [];
myPlaylist.push("Blinding Lights");
myPlaylist.push("As It Was");
myPlaylist.push("Starboy");
console.log(myPlaylist);
// ["Blinding Lights", "As It Was", "Starboy"]
console.log(`${myPlaylist.length} songs in your playlist`);
// 3 songs in your playlist
[...arr]. Spread operator push elements into the array without mutating it.
const playlist = ["Starboy", "Blinding Lights"];
// Instead of playlist.push("Flowers")
const newPlaylist = [...playlist, "Flowers"];
console.log(playlist); // ["Starboy", "Blinding Lights"] (Safe!)
console.log(newPlaylist); // ["Starboy", "Blinding Lights", "Flowers"]
pop(): Remove from the end
pop() removes the last element and returns that removed element.
// BEFORE
const playlist = ["Starboy", "Blinding Lights", "As It Was"];
console.log(playlist); // ["Starboy", "Blinding Lights", "As It Was"]
const removed = playlist.pop();
// AFTER
console.log(playlist); // ["Starboy", "Blinding Lights"]
// the song that was removed
console.log(removed); // "As It Was"
Real example: remove the last song you added:
const playlist = ["Blinding Lights", "Starboy", "Lucid Dreams"];
function undoLastAdd() {
const removed = playlist.pop();
console.log(`Removed: ${removed}`);
console.log(`Playlist now has ${playlist.length} songs`);
}
undoLastAdd(); // Removed: Lucid Dreams | Playlist now has 2 songs
undoLastAdd(); // Removed: Starboy | Playlist now has 1 song
shift() and unshift()
These work just like push/pop but operate on the beginning of the array.
shift(): Remove from the beginning
// BEFORE
const queue = ["Blinding Lights", "As It Was", "Starboy"];
console.log(queue); // ["Blinding Lights", "As It Was", "Starboy"]
// ACTION
const nowPlaying = queue.shift();
// AFTER
console.log(queue); // ["As It Was", "Starboy"]
// song that started playing
console.log(nowPlaying); // "Blinding Lights"
Real example: playing songs in order from a queue:
const queue = ["Blinding Lights", "As It Was", "Starboy", "Lucid Dreams"];
function playNext() {
const song = queue.shift();
console.log(`Now playing: ${song}`);
console.log(`Up next: ${queue[0]}`);
console.log(`${queue.length} songs remaining`);
}
playNext();
// Now playing: Blinding Lights
// Up next: As It Was
// 3 songs remaining
playNext();
// Now playing: As It Was
// Up next: Starboy
// 2 songs remaining
unshift(): Add to the beginning
// BEFORE
const queue = ["As It Was", "Starboy"];
console.log(queue); // ["As It Was", "Starboy"]
const newLength = queue.unshift("Blinding Lights");
// AFTER
console.log(queue); // ["Blinding Lights", "As It Was", "Starboy"]
console.log(newLength); // 3
Real example: someone requests a song to play next:
const queue = ["Starboy", "Lucid Dreams", "In The End"];
function playNext(song) {
queue.unshift(song);
console.log(`"${song}" added to the front`);
console.log(`Queue: ${queue.join(", ")}`);
}
playNext("Blinding Lights");
// "Blinding Lights" added to the front
// Queue: Blinding Lights, Starboy, Lucid Dreams, In The End
You can unshift multiple songs at once and they are inserted in order:
const queue = ["Starboy"];
queue.unshift("Song A", "Song B");
console.log(queue); // ["Song A", "Song B", "Starboy"]
push/pop vs shift/unshift
Think of it like a queue at a concert: - New people join at the back (push). People are let in from the front (shift).
| Method | Where | Action | Returns |
|---|---|---|---|
push(item) |
End | Adds item | New length |
pop() |
End | Removes item | Removed item |
unshift(item) |
Beginning | Adds item | New length |
shift() |
Beginning | Removes item | Removed item |
push/pop are faster than shift/unshift on large arrays. When you add or remove from the beginning, JavaScript has to reindex every element. For performance-sensitive code, prefer push/pop when the order doesn't matter.map(): Transform Every Element
map() goes through every item, runs your function on it, and returns a new array with the results. The original is never touched.
How map() works:
Syntax:
// Traditional function
const newArray = original.map(function(element) {
return /* new value */;
});
// Arrow function (preferred)
const newArray = original.map(element => /* new value */);
// With index
const newArray = original.map((element, index) => /* new value */);
Examples
// Double every number
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(numbers); // [1, 2, 3, 4, 5] // unchanged
console.log(doubled); // [2, 4, 6, 8, 10] // new array
// Add a track number to every song
const playlist = ["Blinding Lights", "As It Was", "Starboy"];
const numbered = playlist.map((song, index) => `\({index + 1}. \){song}`);
console.log(playlist); // ["Blinding Lights", "As It Was", "Starboy"] // unchanged
console.log(numbered); // ["1. Blinding Lights", "2. As It Was", "3. Starboy"]
// Convert all song names to uppercase
const songs = ["blinding lights", "as it was", "starboy"];
const uppercased = songs.map(song => song.toUpperCase());
console.log(uppercased); // ["BLINDING LIGHTS", "AS IT WAS", "STARBOY"]
// Extract just the song title from an array of objects
const tracks = [
{ title: "Blinding Lights", artist: "The Weeknd", plays: 4200 },
{ title: "As It Was", artist: "Harry Styles", plays: 3800 },
{ title: "Starboy", artist: "The Weeknd", plays: 5100 }
];
const titles = tracks.map(track => track.title);
console.log(titles); // ["Blinding Lights", "As It Was", "Starboy"]Traditional for loop vs map()
for loop vs map()
const prices = [10, 25, 50, 100];
// for loop
const discounted = [];
for (let i = 0; i < prices.length; i++) {
discounted.push(prices[i] * 0.9);
}
console.log(discounted); // [9, 22.5, 45, 90]
// map
const discounted2 = prices.map(price => price * 0.9);
console.log(discounted2); // [9, 22.5, 45, 90]
Named callbacks for complex logic
// For simple transforms, inline is fine
const doubled = numbers.map(n => n * 2);
// For complex logic, extract a named function
function formatProduct(product) {
return {
id: product.id,
label: `${product.name} โ $${product.price.toFixed(2)}`,
inStock: product.quantity > 0
};
}
const formatted = products.map(formatProduct);
// Cleaner, testable, and shows up by name in stack traces
filter(): Keep Only What You Need
filter() goes through every item and keeps only the ones where your function returns true. It returns a new array which is shorter than or equal to the original. The original is never changed.
How filter() works
Syntax
const newArray = original.filter(element => /* true = keep, false = remove */);
Examples
// Keep only scores above the passing mark
const scores = [4200, 980, 5100, 750, 3800];
const highScores = scores.filter(score => score > 1000);
console.log(scores); // [4200, 980, 5100, 750, 3800]
console.log(highScores); // [4200, 5100, 3800]
// Keep only songs with more than 1000 plays
const tracks = [
{ title: "Blinding Lights", plays: 4200 },
{ title: "Heat Waves", plays: 980 },
{ title: "Starboy", plays: 5100 },
{ title: "Stay", plays: 750 },
{ title: "As It Was", plays: 3800 }
];
const popular = tracks.filter(track => track.plays > 1000);
console.log(popular.map(t => t.title));
// ["Blinding Lights", "Starboy", "As It Was"]
// Keep only songs by a specific artist
const tracks = [
{ title: "Blinding Lights", artist: "The Weeknd" },
{ title: "As It Was", artist: "Harry Styles" },
{ title: "Starboy", artist: "The Weeknd" },
{ title: "Watermelon Sugar", artist: "Harry Styles" }
];
const weekndSongs = tracks.filter(track => track.artist === "The Weeknd");
console.log(weekndSongs.map(t => t.title));
// ["Blinding Lights", "Starboy"]
for loop vs filter()
const scores = [4200, 980, 5100, 750, 3800];
// for loop
const highScores = [];
for (let i = 0; i < scores.length; i++) {
if (scores[i] > 1000) {
highScores.push(scores[i]);
}
}
console.log(highScores); // [4200, 5100, 3800]
// filter()
const highScores2 = scores.filter(score => score > 1000);
console.log(highScores2); // [4200, 5100, 3800]
filter() is the preferred method for search functionality and filtering a product list by price, category, or a search term typed by the user.reduce(): Combine Everything Into One Value
reduce() loops through every element and accumulates them into a single result which can be a number, string, object, or another array.
How reduce() works
Syntax
const result = array.reduce((accumulator, currentValue) => {
return /* updated accumulator */;
}, initialValue);
Step by step functioning:
const plays = [4200, 980, 5100, 750, 3800];
const total = plays.reduce((acc, count) => {
console.log(`acc: \({acc} + count: \){count} = ${acc + count}`);
return acc + count;
}, 0);
// acc: 0 + count: 4200 = 4200
// acc: 4200 + count: 980 = 5180
// acc: 5180 + count: 5100 = 10280
// acc: 10280 + count: 750 = 11030
// acc: 11030 + count: 3800 = 14830
console.log("Total plays:", total); // Total plays: 14830
Practical examples
// Total play count across your playlist
const plays = [4200, 980, 5100, 750, 3800];
const totalPlays = plays.reduce((acc, count) => acc + count, 0);
console.log(totalPlays); // 14830
// Total listening hours across all users
const userHours = [1200, 850, 2100, 630, 1750];
const totalHours = userHours.reduce((acc, hours) => acc + hours, 0);
console.log(totalHours); // 6530
console.log("Average:", totalHours / userHours.length); // Average: 1306
// Total cost of a merchandise order
const cart = [
{ item: "Hoodie", price: 45 },
{ item: "T-Shirt", price: 25 },
{ item: "Cap", price: 20 }
];
const orderTotal = cart.reduce((acc, product) => acc + product.price, 0);
console.log(`Order total: $${orderTotal}`); // Order total: $90
for loop vs reduce()
const plays = [4200, 980, 5100, 750, 3800];
// for loop
let total = 0;
for (let i = 0; i < plays.length; i++) {
total += plays[i];
}
console.log(total); // 14830
// reduce()
const total2 = plays.reduce((acc, count) => acc + count, 0);
console.log(total2); // 14830
// change initial value to notice the changes. remove it, then accumulator initial value becomes the first element.
// it can also return array and objects
forEach(): Loop Without a New Array
forEach() calls a function on every element and returns nothing. Use it when you want to do something with each element (log it, send it, update the DOM) without needing a result back.
Syntax
array.forEach((element, index) => {
// do something โ no need to return
});
Examples
// Print every song in the playlist
const playlist = ["Blinding Lights", "As It Was", "Starboy"];
playlist.forEach(song => {
console.log(`Now playing: ${song}`);
});
// Now playing: Blinding Lights
// Now playing: As It Was
// Now playing: Starboy
// Print songs with their track number
const playlist = ["Blinding Lights", "As It Was", "Starboy"];
playlist.forEach((song, index) => {
console.log(`Track \({index + 1}: \){song}`);
});
// Track 1: Blinding Lights
// Track 2: As It Was
// Track 3: Starboy
// Log every user's listening hours
const users = [
{ name: "Saumya", hours: 1200 },
{ name: "Alex", hours: 850 },
{ name: "Jordan", hours: 2100 }
];
users.forEach(user => {
console.log(`\({user.name} listened for \){user.hours} hours`);
});
// Saumya listened for 1200 hours
// Alex listened for 850 hours
// Jordan listened for 2100 hours
for loop vs forEach()
const playlist = ["Blinding Lights", "As It Was", "Starboy"];
// for loop
for (let i = 0; i < playlist.length; i++) {
console.log(playlist[i]);
}
// forEach, no index variable needed
playlist.forEach(song => console.log(song));
forEach() vs map()
const songs = ["blinding lights", "as it was", "starboy"];
// forEach โ runs code on each item, returns nothing
const result = songs.forEach(song => song.toUpperCase());
console.log(result); // undefined
// forEach always returns undefined
// map โ transforms each item, returns a new array
const uppercased = songs.map(song => song.toUpperCase());
console.log(uppercased); // ["BLINDING LIGHTS", "AS IT WAS", "STARBOY"]
forEach() |
map() |
|
|---|---|---|
| Returns | undefined |
New array |
| Chainable? | No (But can be present at the end of the chain) | Yes |
| Use for | Side effects (logging, DOM, APIs) | Transforming values |
Method Chaining
The array methods become intresting to use when chained together. This is a syntax pattern where multiple methods are linked together in a single statement. It improves the readability of code and the flow. We also eliminate the need to create more variables.
const tracks = [
{ title: "Blinding Lights", artist: "The Weeknd", plays: 4200 },
{ title: "Heat Waves", artist: "Glass Animals", plays: 980 },
{ title: "Starboy", artist: "The Weeknd", plays: 5100 },
{ title: "Stay", artist: "The Kid LAROI", plays: 750 },
{ title: "As It Was", artist: "Harry Styles", plays: 3800 }
];
// Goal: get the titles of all songs with more than 1000 plays
// Without chaining
const popular = tracks.filter(track => track.plays > 1000);
const titles = popular.map(track => track.title);
console.log(titles);
// ["Blinding Lights", "Starboy", "As It Was"]
// With chaining
const titlesChained = tracks
.filter(track => track.plays > 1000)
.map(track => track.title);
console.log(titlesChained);
// ["Blinding Lights", "Starboy", "As It Was"]
Mental Walkthrough:
Need to ADD or REMOVE items?
โโโ End of array : push() / pop()
โโโ Start of array : unshift() / shift()
Need a new array?
โโโ Same length (transformed) : map()
โโโ Shorter (filtered) : filter()
Need a single value from the array?
โโโ reduce()
Just need to loop (no new array)?
โโโ forEach()
| Method | Mutates? | Returns | Use for |
|---|---|---|---|
push(item) |
Yes | New length | Add to end |
pop() |
Yes | Removed item | Remove from end |
unshift(item) |
Yes | New length | Add to beginning |
shift() |
Yes | Removed item | Remove from beginning |
map(fn) |
No | New array, same length | Transform each item |
filter(fn) |
No | New array, equal or shorter | Select items by condition |
reduce(fn, init) |
No | Single value | Aggregate / combine |
forEach(fn) |
No | undefined |
Side effects |





