As you may or may not know, data structures are the key to efficient algorithms and data management. In this article, we'll take a look at the most popular data structures, how to implement them in JavaScript, and how they can be used to your advantage.
What are data structures?
In computer science, a data structure is a data organization, management, and storage format that enables efficient access and modification. More precisely, a data structure is a collection of data values, the relationships among them, and the functions or operations that can be applied to the data.
When I first started programming, I found data structures and algorithms to be incredibly confusing. Hopefully this article will help developers who are new to these concepts understand how to use some of the more common data structures.
Data structures are used to store data in a computer in an organized way so that it can be used efficiently. Different types of data structures are suited to different kinds of applications, and some are highly specialized to specific tasks. For example, trees are particularly well-suited for implementing databases, while compiler implementations usually use hash tables to look up identifiers.
Common Data Structures
There are many ways to store data, but the data structures I'll be talking about today are Arrays, Linked Lists, Hash Tables, and Trees.
Arrays
Arrays are the simplest data structure. They are just a list of items, where each item can be accessed by its index. For example, an array of numbers might look like this:
const arr = [1, 2, 3, 4, 5];
To access an item in the array, we just use its zero-index. For example, to get the third item, we would do this:
const x = arr[2]
Arrays have some drawbacks, though. They are not very flexible, and they can be slow to access items if the array is large.
Linked Lists
Linked lists are a more flexible data structure than arrays. They are made up of nodes, which are connected together like a chain. Each node contains data, and a pointer to the next node in the list. A linked list of numbers could be visualized like this:
1 -> 2 -> 3 -> 4 -> 5
To access an item in the linked list, we just follow the pointers until we reach the desired node.
To create a linked list in JavaScript, we can create a class that maintains a head pointer in the list:
class LinkedList {
constructor() {
this.head = null;
}
}
We can then create nodes for our list. Each node will have a value and a pointer to the next node in the list:
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
We can now create methods on our LinkedList class to add nodes to the list:
class LinkedList {
constructor() {
this.head = null;
}
add(value) {
const node = new Node(value);
if (this.head === null) {
this.head = node;
return;
}
let current = this.head;
while (current.next !== null) {
current = current.next;
}
current.next = node;
}
remove(value) {
if (this.head === null) return;
if (this.head.value === value) {
this.head = this.head.next;
return;
}
let current = this.head;
while (current.next !== null) {
if (current.next.value === value) {
current.next = current.next.next;
return;
}
current = current.next;
}
}
contains(value) {
if (this.head === null) return false;
let current = this.head;
while (current !== null) {
if (current.value === value) return true;
current = current.next;
}
return false;
}
}
We can now create a list and add nodes to it:
const list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);
And we can remove nodes:
list.remove(2);
Finally, we can check if the list contains a certain value:
list.contains(3);
list.contains(4);
Linked lists are more flexible than arrays because we can easily insert and remove items without changing the rest of the list. They can also be slower to access items, though, because we have to follow the pointers.
Hash Tables
Hash tables are used to store key-value pairs. The key is used to look up the value, like a dictionary. A hash table of numbers can be visualized like this:
1 -> 2
3 -> 4
5 -> 6
To implement a hash table in JavaScript, we can use an object to store the key-value pairs. The keys can be used as the object's properties, and the values can be stored as the property values.
For example, we could store a user's name and age in a hash table like this:
const user = {
name: "John",
age: 30
};
To lookup a value in the hash table, we can use the key as the index:
const name = user.name;
const age = user.age;
To add a new key-value pair to the hash table, we can simply create a new property on the object and assign it a value:
user.city = "New York";
To remove a key-value pair from the hash table, we can use the delete operator:
delete user.city;
``
You can also use `Map` to implement hash tables. Using JavaScript's `Map` class for hash tables has some advantages over using a standard object. For instance, `Map` keeps track of the order in which keys are added, which is important for some algorithms. It also provides some handy methods like `forEach()` and `values()` that make iteration more straightforward.
Here's how you might create a hash table with `Map`:
```javascript
const hashTable = new Map();
hashTable.set('foo', 'bar');
hashTable.set('baz', 'qux');
console.log(hashTable.get('foo')); // 'bar'
console.log(hashTable.get('baz')); // 'qux'
Just be careful not to accidentally use the same key twice. If you do, the second value will overwrite the first:
hashTable.set('baz', 'bar');
console.log(hashTable.get('baz'));
Hash tables are very fast to access items, because the key is used to directly look up the value. They are not very flexible, though, because the keys have to be unique.
Trees
Trees are a type of data structure that is used to store data in a hierarchical way. That is, each item has a parent and zero or more children. For example, a tree of numbers might look like this:
1
/ \
2 3
/ \
4 5
Each node has a value, and a link to another node (or null, if there is no child node).
To access an item in the tree, we just follow the path from the root to the desired node.
Here is an example of how this might be implemented in JavaScript:
class Node {
constructor(value) {
this.value = value;
this.child = null;
}
}
class Tree {
constructor() {
this.root = null;
}
addNode(value) {
const node = new Node(value);
if (this.root === null) {
this.root = node;
} else {
let current = this.root;
while (current.child) {
current = current.child;
}
current.child = node;
}
}
removeNode(value) {
if (this.root === null) {
return;
}
let current = this.root;
let parent = null;
while (current.value !== value) {
parent = current;
current = current.child;
if (current === null) {
return;
}
}
if (this.root === current) {
this.root = null;
return;
}
if (parent.child === current) {
parent.child = null;
return;
}
let sibling = parent.child;
while (sibling.child !== current) {
sibling = sibling.child;
}
sibling.child = current.child;
}
search(value) {
if (this.root === null) {
return null;
}
let current = this.root;
while (current.value !== value && current.child) {
current = current.child;
}
return current;
}
}
Here's how we would use the Tree class:
const tree = new Tree();
tree.addNode(1);
tree.addNode(2);
tree.addNode(3);
tree.addNode(4);
tree.search(3);
tree.search(5);
tree.removeNode(3);
Trees are very flexible, because we can easily add and remove items. They can be slower to access items, though, because we have to follow the path from the root.
Conclusion
Data structures are important in programming because they provide a way to store and retrieve data efficiently. Without data structures, it would be very difficult to write programs that could manipulate data in a useful way.
I hope you found this article helpful and that you now have a better understanding of how to use data structures in your own code.
Let me know in the comments if you found this article helpful or if you have any questions.
Be sure to follow me for more like this!