0

I need to display the names of people whose birthday occur in a certain month. The name of the month must still appear, even if there was no birthday that month. (All info is extracted from a text file)

Eg.

January:
Sam
Kate

February:
//no birthday in Feb

March:
Fred
etc

Method:

private Employee[] arr = new Employee[8];//Employee class

public String birthdays() throws ParseException{
        String list = "";
        for(int i = 0; i < arr.length; i++){
            String month = arr[i].getDob().substring(3, 5);
            if(month.equals("01")){
                System.out.println("Jan" + "\n" + arr[i].getName() + " " + arr[i].getSurname());
            }else if(month.equals("02")){
                System.out.println("Feb" + "\n" + arr[i].getName() + " " + arr[i].getSurname());
            }//if
        }//for
        
        return list;
    }//birthdays

The problem is that it seems like that repeating the "if statement" is not really good coding and that if a birthday does not occur in a certain month, the name of the month will not appear.

How would I solve this?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161

5 Answers5

2

java.time

I recommend you use java.time, the modern Date-Time API to avoid the repetition of if-else if conditions. With this, you will reduce the existing code inside your for loop to just this much:

int month = Integer.parseInt(arr[i].getDob().substring(3, 5));
System.out.println(Month.of(month).getDisplayName(TextStyle.SHORT, Locale.ENGLISH) + "\n" + arr[i].getName() + " " + arr[i].getSurname());

Demo:

import java.time.Month;
import java.time.format.TextStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        System.out.println(Month.of(1).getDisplayName(TextStyle.SHORT, Locale.ENGLISH));
    }
}

Output:

Jan

ONLINE DEMO

Learn more about the modern Date-Time API* from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Thanks. That does remove the repetition of the "if statement", but I need to display all the months, and then all the names of the employees whose birthday falls in that month. – user14229161 Aug 21 '21 at 18:44
2

java.time

Like Arvind Kumar Avinash I strongly recommend that you use java.time, the modern Java date and time API, for your date work. It starts in the Employee class, where dob is a LocalDate (not a string; you don’t keep numbers and Boolean values in strings either do you? I hope not).

public class Employee {

    String name;
    LocalDate dob;
    
    /** Convenience constructor accepting dob as string, for example 1986-05-17 */
    public Employee(String name, String dob) {
        this.name = name;
        this.dob = LocalDate.parse(dob);
    }

    public String getName() {
        return name;
    }

    public Month getBirthMonth() {
        return dob.getMonth();
    }
}

In Java 16 and later, write that class more briefly as a record, with or without the constructor seen above.

record Employee ( String name , LocalDate dob ) {}

Now we can do for example:

    Employee[] arr = {
            new Employee("James", "2008-04-15"),
            new Employee("Jennifer", "1984-10-08"),
            new Employee("John", "2003-05-22"),
            new Employee("Linda", "1965-11-29"),
            new Employee("Mary", "1982-06-15"),
            new Employee("Michael", "1942-12-07"),
            new Employee("Patricia", "2004-03-11"),
            new Employee("Robert", "1951-11-09")
    };
    
    Map<Month, List<Employee>> dobPerMonth = Arrays.stream(arr)
            .collect(Collectors.groupingBy(Employee::getBirthMonth));
    
    for (Month m : Month.values()) {
        System.out.println(m.getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH));
        List<Employee> empsThisMonth = dobPerMonth.get(m);
        if (empsThisMonth == null) {
            System.out.println("//no birthday in " + m.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.ENGLISH));
        } else {
            for (Employee employee : empsThisMonth) {
                System.out.println(employee.getName());
            } 
        }
        System.out.println();
    }

I am giving you the full output:

January
//no birthday in Jan

February
//no birthday in Feb

March
Patricia

April
James

May
John

June
Mary

July
//no birthday in Jul

August
//no birthday in Aug

September
//no birthday in Sep

October
Jennifer

November
Linda
Robert

December
Michael

Link

Oracle tutorial: Date Time explaining how to use java.time.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

Use Hashmap:

    HashMap<String, ArrayList<Employee>> filterEmpBirthdays = new HashMap<>();
    for (int i = 0; i < arr.length; i++) {
        String month = arr[i].getDob().substring(3, 5);
        if (filterEmpBirthdays.containsKey(month)) {
            ArrayList<Employee> empBirthday = filterEmpBirthdays.get(month);
            empBirthday.add(arr[i]);
            filterEmpBirthdays.put(month, empBirthday);
        } else {
            ArrayList<Employee> empBirthday = new ArrayList<>();
            empBirthday.add(arr[i]);
            filterEmpBirthdays.put(month, empBirthday);
        }
    }

After that iterate the loop on HashMap

Sibtain Raza
  • 205
  • 2
  • 4
0

Assuming the dob is in dd-mm-yyyy format, try this:

package wonderings;

import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.Test;

public class CollectionsTest {

    @Test
    public void test1() {
        // Get an array of Month names
        final String[] months = new DateFormatSymbols().getMonths();
        // Create a function to convert dob to month of birth
        final Function<Person, String> toBirthMonth = person -> months[Integer.valueOf(person.getDob().substring(3, 5))
                - 1];
        // Synthesize test data
        final List<Person> people = Arrays.asList(new Person("Jane", "20-03-2000"), new Person("Tom", "22-04-1999"),
                new Person("Heroku", "01-01-1987"), new Person("Ivan", "30-04-1978"));
        // Group the Person instances by month of birth
        final Map<String, List<Person>> groupedBy = people.stream().collect(Collectors.groupingBy(toBirthMonth));
        // Now stream the results, printing a header and the either "none" or the list of Persons in name order
        IntStream.range(0, months.length - 1).forEach(month -> {
            System.out.println(months[month]);
            if (groupedBy.containsKey(months[month])) {
                groupedBy.get(months[month]).stream().sorted(Comparator.comparing(Person::getName))
                        .map(person -> "  " + person.getName()).forEach(System.out::println);
            } else {
                System.out.println("none");
            }
        });

    }

    private static class Person {
        private final String name;
        private final String dob;

        public Person(final String name, final String dob) {
            super();
            this.name = name;
            this.dob = dob;
        }

        public String getName() {
            return name;
        }

        public String getDob() {
            return dob;
        }

    }
}

An all Java 8 solution.

  • *An all Java 8 solution.* Not completely. `DateFormatSymbols` is outdated since Java 8. Use the `Month` enum and its `getDisplayName` method (as in my answer for example). And Java 8 or not, using substring and `Integer.parseInt()` for finding the month number is way to manual for my taste and also skips the date validation that I would have wanted to have. – Ole V.V. Aug 22 '21 at 05:56
  • Nice catch on the DateFormatSymbols. As for the date, given the example of a string the solution was correct. I agree with others that string dates are the last thing you want to do. – Earnie Dyke Aug 22 '21 at 13:41
0

You could use a Map to define your months, declare it as a data member of your class:

protected Map<String, String> months = null;

Inside your constructor, instantiate it and fill it:

this.months = new HashMap<String, String>();
this.months.put("01", "Jan");
this.months.put("02", "Feb");
this.months.put("03", "Mar");
this.months.put("04", "Apr");
this.months.put("05", "May");
this.months.put("06", "Jun");
this.months.put("07", "Jul");
this.months.put("08", "Aug");
this.months.put("09", "Sep");
this.months.put("10", "Oct");
this.months.put("11", "Nov");
this.months.put("12", "Dec");

Don't forget to import Map and HashMap. Now, let's refactor your method:

public String birthdays() throws ParseException{
        Map<String, List<String>> birthdays = new HashMap<String, String[]>();
        for (String key : months.keySet()) {
            birthdays.put(key, new ArrayList<String>());
        }
        String list = "";
        for(int i = 0; i < arr.length; i++){
            String month = arr[i].getDob().substring(3, 5);
            birthdays.get(month).push(arr[i].getName() + " " + arr[i].getSurname());
        }

        for (String secondKey : month.keySet()) {
            list += months.get(secondKey) + ":\n";
            for (String line : birthday.get(secondKey)) {
                list += "\t" + line + "\n";
            }
            list += "\n";
        }
        
        return list;
    }//birthdays

Explanation:

  • we first find out which month corresponds to which number
  • then we create an empty map for birthdays, with the keys being the months
  • we get through the names, putting each into the corresponding months
  • we get through the months, display them independently of whether there is any birthday associated to it
  • we display the names related to the month (if any) below the month
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • You’re reinventing the wheel. I recommend that instead you use [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Aug 22 '21 at 17:17
  • @OleV.V. it can be used, that's certainly true. I prefer to answer questions in a way that's instructive for the askers and if they are beginners, then I aim to explain these things in a way that can be understood. If they just call some methods, from an API, then what do they learn? Yet, you are right, there's nothing wrong in using an API. – Lajos Arpad Aug 22 '21 at 17:25
  • 1
    IMHO one of the most important thing for a programmer to learn is exactly using an API. Well, opinions differ, so thanks for presenting yours. PS Isn’t `Map` API too? Just teasing. :-) – Ole V.V. Aug 22 '21 at 20:18
  • 1
    @OleV.V. you are correct, LOL. I think we understand each-other's point though. – Lajos Arpad Aug 24 '21 at 08:03