-1

So basically I have a map that looks like this

HashMap<Integer, HashMap<String, Integer>> girls = 
new HashMap<Integer, HashMap<**String**, Integer>>();'

and I want to return the bolded string and this is my method so far

public String getGirlName(int year, int rank) { //year refers to Key
                                                //rank refers to other integer in value map 
  if (girls.containsKey(year) && girls.get(year).containsKey(rank)){ 
     //shouldn't this return the name 
     return girls.get(year).get(rank); //error here where it says I can't return an integer I have to return a string
     else return null; 
  }
}

I'm not sure how to do the above title

emmynaki
  • 157
  • 2
  • 10
  • 3
    It feels like your inner map should go from Integer -> String, not String -> Integer: `HashMap>`. – yshavit May 11 '17 at 15:56
  • you are using a hashmap in the opposite way. you use keys to grab values, not use values to grab keys. flipping the inner hashmap to map integer to string will fix this poroblem. – RAZ_Muh_Taz May 11 '17 at 15:56
  • 2
    Possible duplicate of [Java Hashmap: How to get key from value?](http://stackoverflow.com/questions/1383797/java-hashmap-how-to-get-key-from-value) – OH GOD SPIDERS May 11 '17 at 15:57

5 Answers5

2

If you can avoid it, don't use a map of maps. There are reasons and situations where you might want to but it's very confusing, as you've seen yourself.

This is not one of the cases where you want to. Just declare a new class to encapsulate this information and store it in a list:

class Girl
{
   // Declare getters if you want
   public final int    year;
   public final int    rank;
   public final String name;

   Girl(int year, int rank, String name){
       this.year = year;
       this.rank = rank;
       this.name = name;
   }
}

Now your data structure isn't a complete mess:

List<Girl> girls = new ArrayList<>();

and your function is super easy:

public String getGirlName(int year, int rank) {
    for (Girl girl : girls) {
        if (girl.year == year && girl.rank == rank) {
            return girl.name;
        }
    }
    throw new RuntimeException("No such girl"); // or whatever
}
Michael
  • 41,989
  • 11
  • 82
  • 128
2

Let's try to see the structure of HashMap<Integer, HashMap<String, Integer>> visually, since it is quite a complex data structure

//             A         B
//         |--------|--------|
//      C                              D
//  |------|---------------------------------------------------------------------|
{
    Integer: {String: Integer, String: Integer, String: Integer, String: Integer},
    Integer: {String: Integer, String: Integer, String: Integer, String: Integer},
    Integer: {String: Integer, String: Integer, String: Integer, String: Integer},
    Integer: {String: Integer, String: Integer, String: Integer, String: Integer}
}

Sections A and C are the keys, B and D are the values.

Now let's see what does .get(year).get(rank);. First, get(year) is called. year is a value in section C, so get(year) returns a value in section D. Next, get(rank) is called. Since it is called on the return value of get(year), rank here is a value in section A. get(rank) will return a value in section D, which are all Integers. Therefore, the compiler error occurs.

To fix this, simply swap the two types in the inner map. You change from HashMap<Integer, HashMap<String, Integer>> to HashMap<Integer, HashMap<Integer, String>>.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • The root problem is that it's "a complex data structure", as you put it. The solution should be to reduce the complexity of the data structure. – Michael May 11 '17 at 16:18
  • 1
    @Michael Well, yeah. That is already mentioned in your answer, so I am just providing an alternative. – Sweeper May 11 '17 at 16:19
  • Alrighty. That's fine but it wouldn't hurt to make it clearer. :) – Michael May 11 '17 at 16:21
  • Your solution can't be applied if different `name` (the `String` key) of the map can have the same `rank` (the `Integer` value)... – Spock May 11 '17 at 16:23
  • @Spock If there really are same ranks. What was the OP expecting to get from `get(rank)`? The OP is obviously trying to get the name from a rank. – Sweeper May 11 '17 at 16:25
  • @Sweeper obviously, from his other question (http://stackoverflow.com/questions/43925910/sorting-a-hashmap-whos-key-is-another-hashmap) it is clear that the rank can't be unique: the rank represents the popularity of a name, as he sais, so two names can share the same rank. – Spock May 11 '17 at 22:14
1

As stated, your code will not compile for more than the reason you mentioned.

I think you are misunderstanding how to use a Map. A Map is used like an glossary in a book. A glossary contains a page number for a topic. In a similar sense, a Map contains a value (v [value]) for a given (k [page number]).

But you get to tell the computer what you want to store for what. So, you need to understand your keys and values so that you can construct your map correctly.

AusME
  • 41
  • 4
  • 1
    A glossary provides definitions of words (which would also be an analogy for a map...). You're thinking of an index. You've also got the key and value mixed up in your analogy. One would go to the index *looking* for a word (the key) and *get* a page number (the value). Your analogy also falls down because there can be multiple pages (values) associated with a single word (key). This is not possible with a standard one-to-one map. – Michael May 11 '17 at 16:15
0

Try

return girls.getOrDefault(year, Collections.emptyMap()).entrySet().stream()
            .filter(e -> e.getValue() == rank)
            .map(e -> e.getKey())
            .findFirst()
            .orElse(null);
bradimus
  • 2,472
  • 1
  • 16
  • 23
  • 1
    Someone who is having difficulty understanding maps is not going to benefit from a solution including lambdas and streams. Play to your audience. – Michael May 11 '17 at 16:02
-1

You can stream the Map doing:

//get the map that match the year:
Map<String, Integer> mapForKeyYear = g.getOrDefault(year, null);

//if the map is not null then filter that map to the rank:

String r = mapForKeyYear
        .entrySet().stream()
        .filter(e -> e.getValue() == rank)
        .map(Map.Entry::getKey)
        .findFirst().orElse(null);
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • @Michael I don see in my answer the use of the ***Collections.emptyMap()*** thanks for the feedbak anyways... – ΦXocę 웃 Пepeúpa ツ May 11 '17 at 16:07
  • 1
    You're right, but `Collections.emptyMap()` is an *improvement* because yours might give a null pointer exception. So I was wrong, it's *almost* identical but worse ;) – Michael May 11 '17 at 16:09