- Full Stack Express
- Pages
- Ultimate Guide to Key-Value Data Structures
JavaScript Objects: Ultimate Guide to Key-Value Data Structures
Data structures are concepts in programming used to organize and manage data in an efficient manner. They serve as the building blocks for creating algorithms and solving complex problems in software.
Understanding data structures and how to use them properly is key to writing efficient, scalable, and performant code.
Let’s explore how to implement different data structures in JavaScript, and the strengths and weaknesses of each one.
Big O Notation
Before diving into the first data structure, there needs to be a strategy or method for measuring the performance and efficiency of algorithms.
Enter Big O.
The Big O notation is used in computer science to describe the upper bound or worst-case performance of algorithms based on how they scale with input size.
By analyzing algorithms from this perspective, comparisons can be done between different data structures and metrics can be calculated based on how long algorithms run (runtime) and how much memory will be consumed (memory usage).
These two cases are also known as time and space complexity.
Big O Blueprint
Here's a general approach to help analyze time and space complexity for various object-related operations:
1. Identify the Operation: Determine the specific operation you want to analyze, such as property access, insertion, deletion, copying, merging, or iterating over properties.
2. Define Input Size: Determine what constitutes the input size for your operation. For object-related operations, this might be the number of properties in the object.
3. Analyze Time Complexity: For time complexity analysis, count the number of basic operations that are executed as a function of the input size. Consider the worst-case scenario. Common operations and their time complexities include:
Accessing a property: O(1)
Inserting/Updating a property: O(1)
Deleting a property: O(1)
Copying an object: O(n)
Merging objects: O(n)
Iterating over properties: O(n)
4. Analyze Space Complexity: For space complexity analysis, focus on how much extra memory is required as the input size increases. This often includes considering additional data structures used during the operation. Common space complexities include:
O(1): Constant space (does not change with input size).
O(n): Linear space (scales linearly with input size).
O(n^2), O(n log n), etc.: Polynomial space (scales with a polynomial of input size).
5. Consider Hidden Complexity: Remember to account for hidden complexities like hash table overhead, hidden properties, and data structures used internally by JavaScript engines.
6. Combine Complexity: For operations involving multiple steps (e.g., merging objects), combine the complexities of each step to get an overall time or space complexity.
7. Compare to Alternatives: Compare the complexity of your chosen operation to alternative approaches to determine the most efficient option for your specific use case.
8. Understand Practical Implications: Keep in mind that Big O notation provides a high-level perspective and doesn't capture constant factors or small input sizes. Actual performance might differ due to JavaScript engine optimizations, hardware, and other factors.
9. Validate Through Profiling: To validate your complexity analysis, use profiling tools to measure the actual execution time and memory usage of your code with different input sizes. Profiling can help you confirm the theoretical complexities you've derived.
Remember that complexity analysis is a skill that improves with practice. As you analyze more operations and gain experience, you'll become better at estimating the time and space requirements of your JavaScript object-related code.
Objects
Objects in JavaScript are a specific type of data structure that consists of a collection of properties, where each property has a name and value (key-value pair). In other programming languages, objects function similarly to dictionaries, maps, or hash tables.
Objects allow quick storage and retrieval of data, and can easily be used to create complex structures and relationships.
Accessing Object Properties
Time Complexity: O(1)
Space Complexity: O(1)
Accessing a property is one of the most common operations when working with JavaScript objects. For example, give an object named person
:
const person = {
firstName: 'John',
lastName: 'Doe',
age: 25
};
The properties for this person
object can be accessed through two different methods:
Dot notation
Bracket notation
person.firstName // dot notation
person[age] // bracket notation
What would be the time complexity for this operation?
Using either notation, the property value can be obtained with one operation.
Now assume that this person
object has many more properties (e.g. 1000), does this change the number of operations for obtaining a specific property value?
The answer is no.
As a result, accessing an object property has constant time complexity, O(1).
When analyzing the space complexity of this operation, it’s generally considered to be O(1) as well. This is because the memory required to store the object's properties and their values is not directly proportional to the number of properties.
There are certainly edge cases such as hash table overhead, hidden properties, and sparse arrays, but the memory overhead is often negligible compared to the actual value being stored in the property.
These cases will be covered in a different guide.
Inserting Object Properties
Time Complexity: O(1)
Space Complexity: O(1)
Building off the same person
object, additional properties can be added with the same two methods:
person.weight = 150; // dot notation
person['hairColor'] = 'black'; // bracket notation
Notice again how the number of operations is still one, regardless of how many properties are already in the person
object.
As a result, property insertion has a constant time complexity, O(1).
Deleting Object Properties
Time Complexity: O(1)
Space Complexity: O(1)
Does our Big O analysis change when deleting object properties?
Assume the same properties from the previous example were to be deleted:
delete person.weight; // dot notation
delete person['hairColor']; // bracket notation
Again, the number of operations is one and again, the number of properties doesn’t affect the outcome.
Deletion also has a constant time complexity, O(1).
Searching Object Properties
There are a few different ways to search for an object property, some more well known than others. Let’s briefly go over each method and what the time and space complexity is of each one.
Object.hasOwnProperty()
Time Complexity: O(1)
Space Complexity: O(1)
The hasOwnProperty()
method exists on all objects and can be used to check whether an object has a specific property. A boolean value will be returned based on if the property exists or not.
Since a direct lookup (access) is being done with the provided key (property name), the time complexity is O(1) as the time taken doesn’t grow as the size of the object increases. The space complexity is also O(1) since this method only requires a small constant amount of memory to perform the property lookup.
const doesPropExist = person.hasOwnProperty('age');
console.log(doesPropExist); // true
For…In Loop
Time Complexity: O(n)
Space Complexity: O(1)
The for…in
loop iterates over all enumerable properties of an object, including properties inherited from its prototype chain. With each iteration, a comparison can be made to see if there’s a matching property name.
Since this is a linear scan of the object’s properties (going through all the properties one by one), the time complexity is proportional to the number of properties in the object, O(n).
Space complexity, on the other hand, is O(1) as the memory used by the loop itself remains constant regardless of the number of properties in the object.
for (const key in person) {
// loop through keys
}
Object.getOwnPropertyNames()
Time Complexity: O(n)
Space Complexity: O(n)
The Object.getOwnPropertyNames()
method returns an array of all own property names of an object, including enumerable and non-enumerable properties. As a result, the time complexity is linear, O(n).
The space complexity is different for this method as it consumes memory proportional to the number of properties in the object. This results in a space complexity of O(n).
const propNames = Object.getOwnPropertyNames(person);
for (const name of propNames) {
// loop through property names
}
Object.keys()
Time Complexity: O(n)
Space Complexity: O(n)
Object.keys()
functions similarly to Object.getOwnPropertyNames()
but retrieves only own enumerable property names of an object and does not consider properties from the prototype chain.
The time and space complexity are both O(n).
const keys = Object.keys(person);
for (const key of keys) {
// loop through keys
}
Object.values()
Time Complexity: O(n)
Space Complexity: O(n)
The Object.values()
method returns an array containing the values of all enumerable properties of the given object. Because the method needs to iterate over all enumerable properties in the object to construct the array of property values, the time complexity is linear, O(n).
Like Object.keys()
, the array of values grows as the number of properties in the object grows. The space complexity is also O(n).
const values = Object.values(person);
for (const value of values) {
// loop through values
}
Object.entries()
Time Complexity: O(n)
Space Complexity: O(n)
The Object.entries()
method returns an array of [key, value]
pairs for all enumerable properties of an object.
Notice a pattern?
Anytime there is iteration over an object’s properties with an array as the return value, the time and space complexity will be linear, O(n).
const values = Object.entries(person);
for (const [key, value] of entries) {
// loop through entries
}
Reflect.ownKeys()
Time Complexity: O(n)
Space Complexity: O(n)
Reflect.ownKeys()
is much less used compared to the other methods. It returns an array of both string and symbol keys regardless of enumerability and has both linear time and space complexity.
const keys = Reflect.ownKeys(person);
for (const value of values) {
// loop through values
}