The Two Synchronised Arrays Problem
Imagine we have a system for managing users. Within the system source code, two separate arrays hold information on users. One contains unique identifiers for users, represented by integer user ids:
[1, 7, 9, 4, 12]
The second array contains the names of the users:
["Dave", "Carol", "Alice", "Eddie", "Bruce"]
The elements at a given index correspond to the same user’s information in both arrays. Therefore, the user “Alice” at (zero-based) index 2 in the names array has a user id of 9, also at index 2, in the user ids array.
The order of the elements in both arrays must be synchronised. If we remove the user with id = 9 at position 2 within the user ids array, we must remove it from the user names array also at position 2. The resulting arrays would see id = 9 and name = “Alice” spliced from both arrays at position 2:
user ids:
[1, 7, 4, 12]
and user names:
["Dave", "Carol", "Eddie", "Bruce"]
Two Synchronised Array Problem
Can you see the problems we might run into with this flawed design?
What will happen when another programmer, unaware of the implicit relationship between the arrays, decides to reorder the user names in alphabetical order? Maybe they want to display the user names in alphabetic order in the UI:
reordered user names:
["Alice", "Bruce", "Carol", "Dave", "Eddie"]
Yet the user ids array remains unchanged (!):
user ids:
[1, 7, 9, 4, 12]
When we remove id = 9 at index 2, “Carol” — rather than “Alice” — will be deleted from the user names array. Oops.
Not only are we removing the wrong user name (“Carol”), but we are also retaining the user name we should have removed (“Alice”).
Why does this problem come about?
Even though the intent for these two arrays is to keep the ordering of the elements synchronised, no mechanism exists to ensure this.
OK, how do we fix this problem?
A better way is to house related user ids and names together in an object, user, and then group those into a collection of users:
[
{"id": 1, "name": "Dave"},
{"id": 7, "name": "Carol"},
{"id": 9, "name": "Alice"},
{"id": 4, "name": "Eddie"},
{"id": 12, "name": "Bruce"}
]
Why is this better? By combining the related user information into objects removes the need for order synchronisation between two arrays. If we want to delete the user with id = 9, we do so, and the entire user object disappears:
users with
{"id": 9, "name": "Alice"}
in position 2 removed:
[
{"id": 1, "name": "Dave"},
{"id": 7, "name": "Carol"},
{"id": 4, "name": "Eddie"},
{"id": 12, "name": "Bruce"}
]
And if we want to reorder the user objects by name alphabetically, then the user ids get reordered too:
users ordered by name:
[
{"id": 9, "name": "Alice"},
{"id": 12, "name": "Bruce"},
{"id": 7, "name": "Carol"},
{"id": 1, "name": "Dave"},
{"id": 4, "name": "Eddie"}
]
In this way, we overcome the synchronisation problem caused by having two separate yet related arrays.
Is there a general rule or principle to help us avoid such problems?
Yes, there is: The Single Responsibility Principle (SRP) from The SOLID Principles:
“Gather together those things that change for the same reason, and separate those that change for different reasons.”
Regarding the SRP, many of us — myself included — primarily consider what we should separate — like UI & database. Yet, it’s just as important, if not more so, to place together functions and data structures that change simultaneously and for the same reasons — like our user ids and user names.
If you enjoyed this article, please leave some claps — and a bunch of claps if you loved it! :) Thank you kindly.
Join my email list to fast-track your software engineering career.
When signing up, you’ll get my guide, ‘The Road to Master Progammer’, containing 3 powerful ideas to help you shorten your journey to expert programmer.