An array of objects vs objects of objects

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?

color characteristics interface
Product details card

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;
}
data storing method representation of object and array

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

memory block of single element

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

memory blocks of multiple elements

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
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 element from an array

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.