Each solution has its use cases.
I think the first solution is good if you’re trying to define a one-to-one relationship (such as a simple mapping), especially if you need to use the key as a lookup key.
The second solution feels the most robust to me in general, and I’d probably use it if I didn’t need a fast lookup key:
- It’s self-describing, so you don’t
have to depend on anyone using
people to know that the key is the id of the user. - Each object comes self-contained,
which is better for passing the data
elsewhere – instead of two parameters
(id and name) you just pass around
people. - This is a rare problem, but sometimes
the key values may not be valid to
use as keys. For example, I once
wanted to map string conversions
(e.g., “:” to “>”), but since “:”
isn’t a valid variable name I had to
use the second method. - It’s easily extensible, in case
somewhere along the line you need to
add more data to some (or all) users.
(Sorry, I know about your “for
argument’s sake” but this is an
important aspect.)
The third would be good if you need fast lookup time + some of the advantages listed above (passing the data around, self-describing). However, if you don’t need the fast lookup time, it’s a lot more cumbersome. Also, either way, you run the risk of error if the id in the object somehow varies from the id in people.