HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaModerate

Counting persons in each age range

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
agecountingeachrangepersons

Problem

I am calculating the number of people within a given age range. This is my current approach.

Note:

  • The persons is a List



  • The getDiffYears() returns the number of years between now and DOB of the person. It includes the month and day as well.



  • The method will be returning a HashMap() (Can be changed if there is a better alternative.)



The code has been tested and is working. I wanted to know if there is a more efficient approach without using this many if statements?

for (Person p: persons) {
 Date dob = p.getDob();
 if (dob != null) {
  years = getDiffYears(p.getDob());
  if (years < 10) {
   a++;
  } else if (years < 20) {
   b++;
  } else if (years < 30) {
   c++;
  } else if (years < 40) {
   d++;
  } else if (years < 50) {
   e++;
  } else if (years < 60) {
   f++;
  } else if (years < 70) {
   g++;
  } else if (years < 80) {
   h++;
  } else if (years < 90) {
   i++;
  } else if (years < 100) {
   j++;
  } else {
   k++;
  }
 } else {
  k++;
 }
}
map.put("00 - 09", a);
map.put("10 - 19", a);
map.put("20 - 29", b);
map.put("30 - 39", c);
map.put("40 - 49", e);
map.put("50 - 59", f);
map.put("60 - 69", g);
map.put("70 - 79", h);
map.put("80 - 89", i);
map.put("90 - 99", j);
map.put("Unknown", k);

Solution

Java 8 streams

Aside from the typos pointed out by @SuperBiasedMan, you can use Java 8's stream-based processing as an alternative for generating your Map result.

private static final int UPPER_LIMIT = 100;
private static final String[] KEYS = new String[]{ "00 - 09", /* ... */ "Unknown" };

private static int categorize(int year) {
    return Math.min(year, UPPER_LIMIT) / 10;
}

Map results = persons.stream()
                    .map(Person::getDob)
                    .filter(Objects::nonNull)
                    .map(ThisClass::getDiffYears)
                    .map(ThisClass::categorize)
                    .collect(Collectors.groupingBy(ageCategory -> KEYS[ageCategory],
                                Collectors.counting()));


  • Define the UPPER_LIMIT of your age categorization and the desired category names.



  • From a Stream of your Person objects, map() to their birthdays by calling Person::getDob as method reference.



  • filter() null birthdays by using Objects::nonNull.



  • map() birthdays to ages by calling your getDiffYears(Date) method. Note that if it is not a static method, the method reference then becomes this::getDiffYears.



  • map() ages to the age category by calling categorize(int) to get the desired array indices for KEYS. If you have not realized yet, ThisClass is just a placeholder for the actual class name.



  • collect() the age categories into the desired Map result groupingBy() our categorization and counting() the elements per category.



Other advice

  • Your method should return a Map instead of HashMap so that callers of this method only need to deal with the interface, and not the implementation.



  • You may want to consider how to handle for null inputs to getDiffYear(Date). Should it return a negative value?



  • You can also experiment with deriving the category names in a programmatic manner, instead of using literal Strings.



  • You can even think about making your age categorization depend on a configurable upper limit (100) and band (10). For example, how easy will it be to treat the upper limit as 101 (UPPER_LIMIT = 101) and a band of 11 years per category (hint: take a closer look at the suggested code...)?

Code Snippets

private static final int UPPER_LIMIT = 100;
private static final String[] KEYS = new String[]{ "00 - 09", /* ... */ "Unknown" };

private static int categorize(int year) {
    return Math.min(year, UPPER_LIMIT) / 10;
}

Map<String, Long> results = persons.stream()
                    .map(Person::getDob)
                    .filter(Objects::nonNull)
                    .map(ThisClass::getDiffYears)
                    .map(ThisClass::categorize)
                    .collect(Collectors.groupingBy(ageCategory -> KEYS[ageCategory],
                                Collectors.counting()));

Context

StackExchange Code Review Q#116228, answer score: 11

Revisions (0)

No revisions yet.