Hello everyone, Today I’m going to share a magical trick that helps your code run faster. Unlike Java, JavaScript does not have a built-in library to support manipulating data with less amount of time complexity. But there is an alternative way available. By using this trick, you can retrieve / edit/append/search elements in O(1) time complexity. Can’t you believe it? No, matter about best / worst / average case scenarios. After knowing this trick, you can get your target element at O(1) accessing time from collections.
Let me take you to the actual content. As front-end developers, we have to store the API response into a variable to perform future actions such as displaying data, manipulating the data, and sending it back to the server. To explain this concept, I’m going to take a simple example.
The requirement is, need to display product features as a group of buttons. Users can select a feature at a time. Based on selected features products price will be changed. At any time, a user may deselect a feature and select a different feature. This time we have to minus old feature cost and need to add new feature cost. Consider the below example, based on the product color, the cost of the product will change. Each color has a unique ID, name, price, and price unit. So, what data structure do you prefer for this scenario?
Let me guess. Yep, Of course, you may prefer an array of objects. Good. But every edit, search, and filter take O (N) time complexity which means the entire array has to be traversed from beginning to end to get the targeted element. We can avoid this scenario by using the Object-of-object structure. Typescript and JavaScript support dynamic ways of adding keys to an object. So, at runtime, we can create a new property by using the below interface. But we need to decide which attribute going to be a property name. Because it is the unique identifier to access your data.
interface Characteristics {
colorId: string;
colorName: string;
price: string;
priceUnit: string;
}
interface Colors {
[colorId: string]: Characteristics;
}
Objects are also known as dictionaries in other programming languages. An object is a key–value pair. If we know the key, the accessing elements will be simple and will take a constant amount of time for any input.
Array can store a collection of elements under a variable name. To access the array element, we need to know which index the targeted element is present. Arrays offers a set of operations to get index / get element/searching etc.
Let’s discuss how objects are different from arrays.
Adding data into an array vs adding data into an object
Adding elements into an array
After fetching the list of characteristics from the API response, if needed, we can do some parsing or we can directly store it into a redux. To store data in array structure, every object need to be pushed into an array.
Initial stage
const characteristics: Array<Characteristics> = [
{
colorId: "blue_001",
colorName: "Blue",
price: "330",
priceUnit: "dollar",
},
];
Memory representation
Adding multiple elements into an array
const characteristics: Array<Characteristics> = [
{
colorId: "blue_001",
colorName: "Blue",
price: "330",
priceUnit: "dollar",
},
];
listOfCharacteristics.forEach((character) => {
characteristics.push(character);
});
Memory representation
The initial stage array is allocated with a memory address of 1000. Later, Once we start pushing items into an array, it will extend the memory location.
Adding elements into an object
Init stage
const characteristics: Colors = {
"blue_001": {
colorId: "blue_001",
colorName: "Blue",
price: "330",
priceUnit: "dollar",
},
};
Adding multiple properties into an object
const characteristics: Colors = {
"blue_001": {
colorId: "blue_001",
colorName: "Blue",
price: "330",
priceUnit: "dollar",
},
};
listOfCharacteristics.forEach((character) => {
characteristics[character.colorId] = character;
});
Memory representation of object
Every object key is linked with a value using a hash function. A new property will be linked to the object by linking the memory reference.
Adding a new item into an array and object requires O (1) complexity.
Displaying content in UI
We have to render features into the UI. Here, it’s a group of the same component. Ultimately, we have to use the iteration function to display every feature as a separate button. So, there is no way, we all end up with O(N) complexity.
Displaying array data in UI
export default function ProductFeatures({}) {
const [characteristics, setCharacteristics] = useState<
Array<Characteristics>
>([]);
return characteristics.map((spec) => (
<SpecCard colorId={spec?.colorId} colorName={spec?.colorName} />
));
}
Displaying object data in UI
export default function ProductFeatures({}) {
const [characteristics, setCharacteristics] = useState<Colors>({});
return Object.keys(characteristics).map((specKey) => (
<SpecCard
colorId={characteristics[specKey].colorId}
colorName={characteristics[specKey].colorName}
/>
));
}
Manipulate data
Once, every child is rendered into UI, it’s important to handle events which are triggered by children. We need to uniquely identify which child triggered the onClick or onChange event. So based on that, we need to perform the next action. From our example, the user triggers the onClick event of the feature button. If a red button clicks, we need to find out what is the price for red and append the price with the product.
Description of manipulating array data
In this example, Every object has a unique colorId. We can use the indexOf() or find() method to get the color price.
const handleSelect = (colorId: string) => {
let char: Array<Characteristics> = [];
const selectedColorIndex = characteristics.indexOf(
(spec: Characteristics) => spec.colorId === colorId
);
if (selectedColorIndex >= 0) {
const price = characteristics[selectedColorIndex].price;
}
};
Time complexity – O(N)
Description of manipulating object data
We passed the key (colorId) to the child component. So, explicitly we know key. Then, We can directly access the color price like below
const handleSelect = (colorId:string) =>{
if (colorId in characteristics){
const price = characteristics[colorId].price;
}
}
Time complexity – O(1)
Hint: Since you’re going to use a unique name as an object property name, this can be used as a key while rendering multiple children.
Delete actions
Deleting an element from an array
If your targeted element presents at the end of the array. We can simply delete that particular element by using pop() method. It requires O (1) time complexity. It’s a best-case example. If you want to delete an element at the beginning or middle of the array, then the system have to shift the rest of the elements positions too like below screenshot.
Deleting a property from an object is simple. Using the delete keyword we can delete a property from an object and the object should be in mutable state.
Deleting a element from an array
const handleDelete = (colorId: string) => {
const filteredPrice = characteristics.filter(
(spec: Characteristics) => spec.colorId !== colorId
);
};
Time complexity – O(N)
Deleting a property from an object
const handleDelete = (colorId: string) => {
if (colorId in characteristics) {
delete characteristics.colorId;
}
};
Time complexity – O(1)
Conclusion
I have explained what I have learned from my previous experience. Ultimately choosing an array of objects or objects of objects depends on your use case. Object of object method will be useful when you need to manipulate data based on user events. It will reduce number of lines of code and it may difficult to understand code flow at first time.