The Two Synchronised Arrays Problem

Imagine we have a system for managing users. Say, within the system source code, two separate arrays hold information on users. One array contains unique identifiers for users, represented by integer ids:

user ids:

[ 1, 7, 9, 4, 12 ]

The second array contains the names of the users: 

user names:

[ "Dave", "Carol", "Alice", "Eddie", "Bruce" ]

The elements at a given index correspond to the same user’s information in both arrays. Therefore, 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 ]

user names:

[ "Dave", "Carol", "Eddie", "Bruce" ]

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 ]

Now, 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, there exists no mechanism to ensure this.

 

OK, how do we fix this?

A better way is to house related user ids and names together in an object:

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" }
]

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 things that change for different reasons.”

When it comes to the SRP, many of us—myself included—primarily consider the things 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 at the same time and for the same reasons—like our user ids and user names.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply