1

I am using a JSON map to save objects (e.g. users) whose ids are the keys of the map but the object (user) itself also contains the id.

JSON

{
   "john":{
      "username":"john",
      "age":30
   },
   "bob":{
      "username":"bob",
      "age":40
   },
   "chris":{
      "username":"chris",
      "age":50
   }
}

Class

class user {
   String username;
   Integer age;

   getUsername() {
      return username;
   }
}

I want to use the map to retrieve the objects easily but at the same time I don't want to have ids as keys in the map and as attributes of the object. What is the cleanest solution for this?

I can think of the following solutions:

  1. Don't use map but list. But then accessing the elements will need an iteration over the list and I can't use simple get(K key) methods.
  2. Don't save username in object But then getUsername() will need to know where it was saved in the map which will imho be really bad coupling.
  3. Use enums for usernames But then users can not be dynamically read from JSON.
  • 5
    What's wrong with having ids as keys in the map and attributes of the object? – Dan Pichelman Dec 27 '16 at 20:15
  • I save the same information twice which feels wrong. – mrplow Dec 27 '16 at 20:17
  • 4
    Are there any reasons not to use integer-based IDs (either within the lifetime of a running application or uniquely assigned by a database) – rwong Dec 27 '16 at 20:47
  • slightly OT but I suggest you to use incremental integer ids, I always found "talking codes" sooner or later bites you back. ie: if you have a history of user actions, and then a user changes his username ? – simone cittadini Dec 28 '16 at 10:37
  • @rwong The main advantage of using the username as keys is more convenient access to the elements. Instead of using integer ids I could simply move from a map to a list or array. A map with integer ids does not provide any benefits. – mrplow Dec 28 '16 at 11:46
4

The user is an entity of your application and as such it should contain all the necessary fields that represent a real user.

The user entity has absolutelly nothing to do with various structures you use throughout your application for performance, display, persisting, etc. Elements in a collection should not be aware of the collection in which they are held. Just because a map needs a key to store a value, it does not mean you have to do something to the value to remove duplication.

You are overthinking this. There is no problem with having the username in the JSON representation. As your application grows you will see that working with entities instead of individual fields makes your API/code more intuitive.

3

Consider separating your concerns. Right now you're saving your indexed data. However, the indexing of the data is an implementation detail. What if you find that you're retrieving based on age often enough that you want to index based on that?

Instead, save as an array, and when you load the data you perform the indexing. Process the data into a useful structure in memory, where duplication is acceptable. When you save, remove the duplication. This minimizes the amount of information saved to disk or transmitted over the wire. Remember that even today, CPU is orders of magnitude faster than disk, which is orders of magnitude faster than internet.

0

First, let me say that I completely agree with answers provided by @Bogdan and @KevinFee. Having said that, my answer will just expand on their answers by providing a suggestion on how you can achieve this.

Consider using the following approach:

class User
{
    public int Age { get; set; }
}

class IdentifiedUser : User 
{
    public string Username { get; set; }

    public IdentifiedUser(User user, string username)
    {
        Username = username;
        Age = user.Age;
    }

    public User CreateUser()
    {
        return new User
        {
            Age = this.Age
        };
    }
}

class UserRepository
{
    public Dictionary<string, User> Repository = new Dictionary<string, User>();

    public void AddUser(IdentifiedUser identifiedUser)
    {
        Repository.Add(identifiedUser.Username, identifiedUser.CreateUser());
    }

    public IdentifiedUser GetUser(string username)
    {
        return new IdentifiedUser(Repository[username], username);
    }
}

Now obviously, I omitted some of the good practices like the checks for existence of the key in the dictionary because I want to focus on the problem at hand.

The idea here is to have the repository of objects that do not have redundant information in them, but to also provide a mechanism to provide those objects to the outside world with the identification data included. This could also be achieved through association rather than inheritance. That way, you minimize the disk space, at the cost of negligible CPU overhead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.