Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Java Java Data Structures Exploring the Java Collection Framework Maps

Devin Scheu
Devin Scheu
66,191 Points

getCategoryCounts() help

Question: In Blog.java add a new method called getCategoryCounts. It should return a Map of category to count calculated by looping over all the posts.

I need a step by step walk through of this challenge, because i really want to understand what is going on.

Code:

com/example/BlogPost.java
package com.example;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class BlogPost implements Comparable<BlogPost>, Serializable {
  private String mAuthor;
  private String mTitle;
  private String mBody;
  private String mCategory;
  private Date mCreationDate;

  public BlogPost(String author, String title, String body, String category, Date creationDate) {
    mAuthor = author;
    mTitle = title;
    mBody = body;
    mCategory = category;
    mCreationDate = creationDate;
  }

  public int compareTo(BlogPost other) {
    if (equals(other)) {
      return 0;
    }
    return mCreationDate.compareTo(other.mCreationDate);
  }

  public String[] getWords() {
    return mBody.split("\\s+");
  }

  public List<String> getExternalLinks() {
    List<String> links = new ArrayList<String>();
    for (String word : getWords()) {
      if (word.startsWith("http")) {
        links.add(word);
      }
    }
    return links;
  }

  public String getAuthor() {
    return mAuthor;
  }

  public String getTitle() {
    return mTitle;
  }

  public String getBody() {
    return mBody;
  }

  public String getCategory() {
    return mCategory;
  }

  public Date getCreationDate() {
    return mCreationDate;
  }
}
com/example/Blog.java
package com.example;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class Blog {
  List<BlogPost> mPosts;

  public Blog(List<BlogPost> posts) {
    mPosts = posts;
  }

  public List<BlogPost> getPosts() {
    return mPosts;
  }

  public Set<String> getAllAuthors() {
    Set<String> authors = new TreeSet<>();
    for (BlogPost post: mPosts) {
      authors.add(post.getAuthor());
    }
    return authors;
  }
}

Mod edit: Added clarification to title.

11 Answers

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Devin;

I'll walk you through my code based on my approach outline and show you what I came up with...

  • Define a Map variable to put in our category counts, Map categoryCounts = // some sort of interface for Map
Map<String, Integer> categoryCounts = new HashMap<String, Integer>
  • Implement a for loop that goes through our blog posts you could use something like (BlogPost post : mPosts)
for (BlogPost post : mPosts) {}
  • Get our category, which should be a string. Using the above syntax we could use post and the getCategory() method.
String category = post.getCategory();
  • Count the categories in our Map and increment a counter based on their number.
Integer count = categoryCounts.get(category);
if (count == null) {
  count = 0;
}
count ++;
categoryCounts.put(category, count);
}
  • Return our map.
return categoryCounts;
  • Important! Remember to add any new import statements.

Since we have used Map & HashMap we need to make sure we import those.

import java.util.Map;
import java.util.HashMap;

Just to condense everything into the actual function you would get...

  public Map<String, Integer> getCategoryCounts() {
    Map<String, Integer> categoryCounts = new HashMap<String, Integer>();
    for (BlogPost post : mPosts) {
        String category = post.getCategory();
        Integer count = categoryCounts.get(category);
        if (count == null) {
          count = 0;
        }
        count++;
        categoryCounts.put(category, count);
    }
    return categoryCounts;
  }

Don't forget the imports though. :smile:

I hope that helps and makes some sense. Post back if anything isn't clear.

Ken

Hi Ken,

Will you please walk me through the line where you initialize count as an Integer? I don't understand what that line is doing (other than setting the variable type) at all.

Thanks.

Ken Alger
Ken Alger
Treehouse Teacher

Rulon Taylor -

I'll give it a shot at least...

Integer count = categoryCounts.get(category);

As you pointed out, we are declaring a variable called count of type Integer. At the same time we are assigning it a value. We want to know how many of each category is in our HashMap categoryCount. The Java HashMap class has a built in method called get() which returns the value to which the specified key is mapped and will accept a String value. Therefore, if we pass categoryCounts.get("Java"), we will get back categories with a value of "Java". The for loop and if statements then handle the looping through all of the categories and increasing the count variable if, for example, there are five "Java" categories.

Does that help at all? If not if you would start a new thread on the topic, that would be great and we can get it ironed out there.

Happy coding,
Ken

Why in the treet example there was a double for loop, but not in this one?

Casey Huckel
Casey Huckel
Courses Plus Student 4,257 Points

Ken, I tried this with the 2 imports (Map and HashMap) and got compiler errors of the class, interface, or enum expected sort. Can you explain?

Thank you for the explanation Ken. This is not my first round with Java, but I am by no means an expert. I am studying for my Oracle Certified Java Associate for my Degree, and I have not even thought of using the HashMaps or Maps Class of the Collections Framework yet (probably due to the fact that I am not a developer.. yet) it was easy to follow though and i saw where I went wrong. Thanks again!

This was actually extremely helpful! There might of been a few reasons I was having a hard time with this (its 3:40 in the morning), but it seemed like my logic behind it was correct.

James Carney
James Carney
16,255 Points

This was very helpful. Thanks for posting this!

Hi Ken, Obviously this is a very old post you offered but in it you mentioned your "approach outline". Is there someplace you can point to that discusses how to create that outline? Or can you show us your approach?

Thanks, I really appreciate your approach to the craft.

Mehmet Arabaci
Mehmet Arabaci
4,432 Points

This may sound stupid but for the for loop why do we use

for (BlogPost post : mPosts) {

and not

for (String post : mPosts) {

Can someone explain with detail, because I'm not quite getting it, why we declare a variable as BlogPost, or another class we created for that matter?

Michael Macdonald
Michael Macdonald
33,128 Points

Hi Ken,

Why do you use:

public Map<String, Integer> getCategoryCounts() { Map<String, Integer> categoryCounts = new HashMap<String, Integer>();

}

and not:

Map<String, Integer> getCategoryCounts() = new HashMap<String, Integer>() {

}

I'm kind of confused on this...?

Ben Summers
Ben Summers
1,306 Points

The logic, written in everyday English is like this (I think!):

  1. Create a Map of the type HashMap to hold our keys (the different categories) and the values (how many times each category appears).

  2. Start a loop going through each of the BlogPosts that are in our array of BlogPosts (which is known in this Blog class as mPosts).

  3. Get the category (as a string) for the BlogPost that you are dealing with in this round of the loop (using the getCategory() method we have already defined).

  4. Create an integer variable ('count') to store the result of a check inside the Map we created at step 1, to see if the category we are dealing with is already in there (use the .get() method on our Map using the category we are dealing with as the variable passed to the method - we want to see how many times this category appears in the Map i.e. what the 'value' for this key already is).

  5. If the category we are dealing with is not already in the Map (i.e. the result of the check comes back as null), set 'count' to zero (i.e. give it a zero value rather than just 'null' which is not an integer).

  6. Otherwise (i.e. if it does already appear), increment the 'count' variable by 1, because we have got a repeated category and we need to increase the value already held in the Map by one.

  7. 'Put' the category we are working with back into the Map together with the value stored in our 'count' variable (which will either be zero - Step 5 - or the existing value plus one - Step 6).

  8. Close the loop (so that it will now go back to step 2 to start again on the next BlogPost in our mPosts array).

  9. Finally, once the loop has finished going through the individual BlogPosts in our array, 'return' the Map we have created.

I got stuck on step 3 - I didn't see how easy it was to work out how many time we had already come across the category before in our loop. I hope explaining it in ordinary language helps a bit (subject to being told by Ken or Craig that I've got it wrong!!).

Good luck!

Ben

Arthur Podkowiak
Arthur Podkowiak
3,633 Points

You know, you have maybe 2 more points than I do but this was an awesome answer. I was so confused as to what Maps actually did, I completely forgot the beginning of the video where he is just checking how many times a hash tag was written. This response was awesome, thanks man!

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Devin;

Okay, next step then...

We have our function defined and now just need to put a bunch of code in there that does what we need, right? Here is the outline of the approach I took.

  1. Define a Map variable to put in our category counts, Map<String, Integer> categoryCounts = // some sort of interface for Map.
  2. Implement a for loop that goes through our blog posts you could use something like (BlogPost post : mPosts).
  3. Get our category, which should be a string. Using the above syntax we could use post and the getCategory() method.
  4. Count the categories in our Map and increment a counter based on their number.
  5. Return our map.
  6. Important! Remember to add any new import statements.

Give it another go and post back with your code if you are still stuck.

Ken

Devin Scheu
Devin Scheu
66,191 Points

Hey ken, you gave a good answer but i still am getting errors, can you walk me through what I did wrong?

 public Map<String, Integer> getCategoryCounts() {
    Map<String, Integer> categoryCounts = new HashMap<String, Integer>();
    for (BlogPost post: mPosts) {
      for (String category: post.getCategory()) {
        Integer count = categoryCounts.get(category);
        if (count == null) {
          count = 0;
        }
        count++;
        return categoryCounts.put(category, count);
      }
    }
  }

I'm still confused about the integer part.

Integer count = categoryCounts.get(category);

How is it that we get a count of the total number of categories? The HashMap categoryCounts is at the time of creation empty right? So how does it return anything to the Integer count? It is also my understanding that the get() method returns the value of its key. How is this indicative of the total number of categories? I am sure something is going completely over my head... :'(

Kartik Gera
Kartik Gera
943 Points

It is not indicative of total number of categories, but number of times that category appears(value of its key in hashmap). We keep updating the count until we run through all categories.

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Devin;

Let's take a look:

Task 1

In Blog.java add a new method called getCategoryCounts. It should return a Map of category to count calculated by looping over all the posts.

Okay, in our new method, getCategoryCounts() we need it to return a category mapped to the number in that category, correct? The output in key/value pairs will wind up being a programmatic representation of something like:

Dogs, 3

Cats, 4

Horses, 17

That looks like we need our map to be a <String, Integer> as a return type therefore our cookie-cutter method definition will be:

public Map<String, Integer> getCategoryCounts() {

    return null;   // Ultimately needs to return a Map<String, Integer>
}

Inside our function then, we need to define a new Map so that we can return it and then loop through our categories, count them, and add the category and count to our map.

Without giving away the entire challenge, does that give you enough to go on to proceed?

Post back if you are still stuck.

Ken

Devin Scheu
Devin Scheu
66,191 Points

Hey im still not getting this :/

Edane Barton
Edane Barton
11,457 Points

You are using two FOR statement. You only need one.

Devin Scheu
Devin Scheu
66,191 Points

You use 2 for statements for this challenge. That is the correct syntax for this challenge even thought it seems a little weird.

Hello ! This is my getCategoryCounts method, I feel like i'm in the right direction but I still miss something, may I have any help? Thanks!

public Map<String, Integer> getCategoryCounts() {
    Map<String, Integer> categoryCounts = new HashMap<String, Integer>();
    for (BlogPost post : mPosts) {
       if ( counter == null ) {
          counter = 0; 
        }
       counter ++;
       categoryCounts.put(post.getCategory(),counter);   
    } 
    return categoryCounts;
}
Edane Barton
Edane Barton
11,457 Points

How codes your code know what category to increment. You are missing that piece.

Ken, GREAT STEP BY STEP EXPLANATION! I did have to go back to add a few symbols that I missed. FYI to Others, be aware of the very last bracket } that should be to the far left.

Scott Chaplinski
Scott Chaplinski
6,542 Points

Great explanation. For some reason I am getting the error

./com/example/BlogPost.java:49: error: cannot find symbol for (BlogPost post : mPosts) { ^ symbol: variable mPosts location: class BlogPost Note: JavaTester.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 error

Darwin Palma
Darwin Palma
3,430 Points

I solved it like this:

  public Map<String,Integer> getCategoryCounts () {
    Map<String,Integer> categories = new TreeMap<String,Integer>();
    Set<String> category = new TreeSet<String>();
    int count = 0;
    for (BlogPost post: mPosts){
      category.add(post.getCategory());
    }
    for (String categ: category){
      int categoryQuantity = 0;
      for (BlogPost post: mPosts){
        if(categ.equalsIgnoreCase(post.getCategory())){
          categoryQuantity++;
        }
      }
      categories.put(categ,categoryQuantity);
      count++;
    }
    return categories;
  }

.put.put.put.put.put.put.put.put.put.put.put.put.put

James Vlok
James Vlok
17,059 Points

Good day, Everyone.

I have come across an interesting bug in this code challenge. When checking my work I get the following error:

"Bummer: For category Entertainment, expected 1 but received 1."

this is the code I used in my getCategoryCounts() method:

public Map<String, Integer> getCategoryCounts(){

    // Store a String as the key and an Integer as the value in a Map
    Map<String, Integer> catCount = new HashMap<String, Integer>();
    // Loop through the List of posts
    for(BlogPost blogPost : mPosts){
        // Create new Integer object to store counts and initialise value to 1
        Integer count= new Integer(1);
        // Get the category from the post
        String category = (blogPost.getCategory());
        // Check if Category exists in the Map, if it does get the value and increment by 1
        if(catCount.containsKey(category)){
            //Get current Category Count
            count = catCount.get(category);
            //Increment the count by 1
            count++;
        }
        // Add Category and Count to the Map
        catCount.put(category,count);
    }
    // Return the Map of Category to counts
    return catCount;
}

Using a print statement:

     catCount.put(category,count);
     System.out.printf("%s  :  %d%n",category,count); 

to debug the category and counts after being add to the map I get the following:

Tech : 1 Tech : 2 Science : 1 Health : 1 Health : 2 Food : 1 Entertainment : 1 Tech : 3 Food : 2 Health : 3

I also checked the Maps content using a forEach loop to print the returned Map before the return statement is executed:

    for(Map.Entry<String, Integer> entry : catCount.entrySet()){
             System.out.printf("%s : %d%n", entry.getKey(), entry.getValue());
    }
    // Return the Map of Category to counts
    return catCount;

I get the following:

Tech : 3
Entertainment : 1 Health : 3 Science : 1 Food : 2

James Vlok
James Vlok
17,059 Points

Using the solution posted above by Ken Alger the print statements I used to debug my code, outputs the same values and returns the same Map as his solution. How ever the bug "Bummer: For category Entertainment, expected 1 but received 1." is not present when submitting the code challenge.