DEV Community

Brendon O'Neill
Brendon O'Neill

Posted on

DSA: Array (JavaScript)

Introduction

Every time I start diving into Data Structures and Algorithms, I tell myself, “This time, I’ll stick with it.” I usually get as far as learning about graphs, and then I find myself distracted by, maybe a project breaks and needs refactoring, or I remember a topic I wanted to research and lose track of my DSA studies. I often tell myself “I do front-end, it’s not that important, I’ll learn it later.” But now that I’ve gone deeper into back-end programming, I keep seeing DSA (Data Structures and Algorithms) mentioned, and I know it’s time to really learn them.

So this post is part of my self-study journey. I’m using it as a reference to recall key concepts as I go, but I also hope it helps others who are starting their DSA studies or, like me, need a refresher.

To begin, I’ll focus on Big O notation, what it means and why it’s important and then look at arrays, one of the most common data structures in JavaScript. Arrays are a great way to connect how time and space complexity actually apply in practice.

Big O notation

When you start looking into DSA, one of the first things you’ll come across is Big O notation. At first, it looks like strange math symbols with a weird graph that people casually throw around, but it’s really just a way to describe how efficient a piece of code is as the amount of data grows.

Big O helps us measure how long it might take to run a piece of code (time complexity) or how much memory it uses (space complexity). It’s not about the exact time your code runs, it’s about how it scales.

The way I like to think about Big O is how we do tasks in life. If I have a list of 20 names in alphabetical order, I could scan the list name by name to find the one I’m looking for. That could work each time, but what if I had a phone book of names and tried to start from the beginning and read every name? That could take hours and isn’t the best way to achieve our goal.

Instead, we look for the first letter of the name and remove 25 sections of the book down to one. Then we do the same for the second letter until we find the name or not. This is like using an algorithm to speed up the search.

Great Site with a clear graph

Constant Time O(1)

Constant time is one of the fastest time complexities, as we don’t need to look over the whole array. Since we already know the index of the value we want, we can directly access that one index to get our value.

let numbers = [2, 4, 6, 8, 10];
console.log(numbers[0]); // 2
Enter fullscreen mode Exit fullscreen mode

Linear Time O(n)

Linear time is like a loop that goes through an array from start to finish. We look at each value one by one. This array can be any size and still be considered linear time, as we will check each value in the array.

let numbers = [2, 4, 6, 8, 10];

for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i]); // 2 4 6 8 10
}
Enter fullscreen mode Exit fullscreen mode

Quadratic Time O(n²)

Quadratic time is when there is a loop within a loop. It takes longer because for each element in the outer loop, we also run through the entire inner loop.

let values = [[1,2,3],[4,5,6],[7,8,9]];

for (let i = 0; i < values.length; i++) {
    for (let j = 0; j < values[i].length; j++) {
        console.log(values[i][j]);
    }
}
// 1 2 3
// 4 5 6
// 7 8 9
Enter fullscreen mode Exit fullscreen mode

When people talk about Big O, most of the focus goes toward time, how fast the code runs, but space complexity is just as important. Space complexity is about how much extra memory your code needs to complete its task. If the logic in your function creates new variables, copies arrays, or stores temporary data, all of that uses extra space. Sometimes it’s unavoidable, sometimes it’s just a side effect of how a problem is solved.

Constant Space O(1)

Constant space uses the same amount of extra memory regardless of the input size. As it is a single value, it is considered constant space, as we don't need to loop over it like an array.

let number = 0;
Enter fullscreen mode Exit fullscreen mode

Linear Space O(n)

Linear space uses memory proportional to the size of the input. As an array gets more values, the larger our memory grows as the array grows.

let numbers = [1,2,3,4,5,6,7,8,9,10];
Enter fullscreen mode Exit fullscreen mode

Quadratic Space O(n²)

Quadratic space is memory usage that grows with the square of the input size. As you create nested arrays, the memory usage grows at a larger rate.

let numbers = [[1,2,3],[4,5,6],[7,8,9],[10]];
Enter fullscreen mode Exit fullscreen mode

Big O notation isn’t just theory. It’s a way to think critically about your code’s performance before those problems show up. When your front-end app slows down or your API response lags, often it’s because something that seemed “fast enough” at a small scale actually has a poor time complexity.

For me, learning Big O is less about memorising every notation and more about developing a sense of scale.

Can this code grow comfortably if my dataset doubles?

Am I storing unnecessary data that takes up too much space?

Once you start asking those questions, you’re already thinking like a DSA-minded developer.

Arrays

Arrays are one of the most common data structures used in JavaScript, but when we talk about them in DSA terms, it’s important to remember that JavaScript arrays are actually array lists. This means they don’t have a fixed size, they grow and shrink as we add or remove elements.

This flexibility is useful, but it also means operations can have different time complexities depending on what we’re doing.

Not every array method performs the same way. Some are nearly instant regardless of array size, while others slow down as the array grows because items need to be shifted or copied in memory.

Push()

Push adds an element to the end of an array. This has a time complexity of O(1) and a space complexity of O(1), as we don’t need to loop over the array or create a new one.

let arr = [1,2,3,4,5];

arr.push(6);
console.log(arr); // [1,2,3,4,5,6]
Enter fullscreen mode Exit fullscreen mode

Pop()

Pop removes the last element from an array. This has a time complexity of O(1) and a space complexity of O(1), as we simply remove the last value without looping or copying data.

let arr = [1,2,3,4,5];

arr.pop();
console.log(arr); // [1,2,3,4]
Enter fullscreen mode Exit fullscreen mode

Shift()

Shift removes an element from the beginning of an array. This has a time complexity of O(n) and a space complexity of O(1) because all remaining values need to shift one position to fill the empty index. The method returns the removed value.

let arr = [1,2,3,4,5];

arr.shift();
console.log(arr); // [2,3,4,5]
Enter fullscreen mode Exit fullscreen mode

Unshift()

Unshift adds a new element to the beginning of an array. This has a time complexity of O(n) and a space complexity of O(1), as all other elements must shift one position to create space for the new value.

let arr = [1,2,3,4,5];

arr.unshift(6);
console.log(arr); // [6,1,2,3,4,5]
Enter fullscreen mode Exit fullscreen mode

Splice()

Splice can add, remove or replace elements in an array. This usually has a time complexity of O(n) and a space complexity of O(n), since the array must shift values to make room or close gaps. When used to remove elements, it returns a new array containing the removed values. If no values are removed, space complexity can be considered O(1).

let arr = [1,2,3,4,5];

arr.splice(2,1);
console.log(arr); // [1,2,4,5]
Enter fullscreen mode Exit fullscreen mode

Slice()

Slice returns a shallow copy of a portion of an array. It has a time complexity of O(n) and a space complexity of O(n), as a new array is created containing the copied section.

let arr = [1,2,3,4,5];

let newArr = arr.slice(2,4);
console.log(newArr); // [3,4]
Enter fullscreen mode Exit fullscreen mode

Map()

Map returns a new array populated with the results of calling a given function on each element. This method has a time complexity of O(n) and a space complexity of O(n), since it creates a new array with the updated values.

let arr = [1,2,3,4,5];

let newArr = arr.map((x) => x * 2);
console.log(newArr); // [2,4,6,8,10]

Enter fullscreen mode Exit fullscreen mode

Filter()

Filter returns a shallow copy of the array elements that pass a test function. It also has a time complexity of O(n) and a space complexity of O(n), as it must loop through all values and store the ones that meet the condition in a new array.

let arr = [1,2,3,4,5];

let newArr = arr.filter((x) => x <= 3);
console.log(newArr); // [1,2,3]
Enter fullscreen mode Exit fullscreen mode

ForEach()

forEach executes a provided function on each element in the array. It has a time complexity of O(n) and a space complexity of O(1), since it only loops through the array and doesn’t create a new one.

let arr = [1,2,3,4,5];

arr.forEach((x) => console.log(x)); // 1 2 3 4 5
Enter fullscreen mode Exit fullscreen mode

Limiting arrays length

One important thing about arrays in JavaScript is that they’re dynamic, meaning new elements can be added or removed at any time. However, this also means that the array might reallocate memory internally as it grows beyond its initial storage size, which can cause small performance hits when scaling up.

If you know the approximate number of elements your array will hold, you can predefine its size to reduce internal resizing operations.

let limit = 5;
let preSizedArray = new Array(limit);
Enter fullscreen mode Exit fullscreen mode

By setting a starting size, JavaScript can allocate enough memory ahead of time, avoiding multiple reallocations as the array grows. This doesn’t create a true fixed array like in lower-level languages, but it makes performance more predictable when dealing with large data sets.

Arrays in DSA Context

In DSA, a traditional array has a fixed size, while a dynamic array (like in JavaScript) automatically resizes itself as new elements are added. That resizing typically happens when the internal storage becomes full, JavaScript allocates a new block of memory and copies all existing elements into it. This resizing process has a cost of O(n) time.

So even though push() is usually efficient, occasionally it can take longer when the array resizes. Understanding this behaviour helps write more stable and optimised code, especially when working with larger data sets.

Conclusion

I hope this was helpful to anyone who, like me, is trying to strengthen their understanding of DSA while coding in JavaScript. Knowing how to measure time and space complexity and how arrays behave underneath can make a big difference when working on larger projects.

This post was mainly for my self‑study, but if it helps you think a bit more about performance and scalability, then that’s even better.

Top comments (0)