Skip to main content

Command Palette

Search for a command to run...

Level Up Your JavaScript: A Practical Guide to Array Manipulation

Published
โ€ข14 min read
Level Up Your JavaScript: A Practical Guide to Array Manipulation
S
Trying to transition my career to explore new things, new tech

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
๐Ÿ’ก
It is adviced to avoid changing data in an array. Prefer non-mutating patterns whenever possible.

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
๐Ÿ’ก
Another way to push elements into the array can be through spread operators [...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
๐Ÿ’ก
It is a good practice to always provide the initial value in reduce method.

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

References

Simply JavaScript

Part 19 of 25

JavaScript is a quirky language. To master it, one should know to avoid its hidden traps along with its logic. This series showcase my journey through JS: the pain points, the breakthroughs, and the coding standards that I adopted from my mentors.

Up next

The Ultimate Guide to JavaScript Arrays for Beginners

You are trying to make a personlised music app, adding your favourite songs in a playlist. You have no idea about arrays. So, you would store your songs like: let song1 = "Blinding Lights"; let song2