alo.fernando
alo.fernando

Reputation: 45

Instead of multiple if statements Java date

Is there a better way to write this instead of writing multiple if statements?

I'm parsing through a document to find the instances of date and incrementing the int if an instance occurs.

public class OrganisingData {

    static int jan16=0;
    static int feb16=0;
    static int mar16=0;//... static int dec18

    public static void Months(String dates) {
        if (dates.substring(2, 4).equals("16") && 
            dates.substring(5,7).equals("01")) {
            jan16++;
        }
        if (dates.substring(2, 4).equals("16") && 
            dates.substring(5,7).equals("02")) {
            feb16++;...
        }
        if (dates.substring(2, 4).equals("18") &&
            dates.substring(5,7).equals("12")) {
            dec18++;
        }
    }
}

I am trying to build a bar chart and jan16 feb16 etc represent the month and the year and each time i find an insistence of that date (eg. 2016-01-15) i would increment jan16. so instead of writing multiple if statements for each month + year (total of 32 if statements)is there a better way to write this?

Upvotes: 1

Views: 383

Answers (5)

O.O.Balance
O.O.Balance

Reputation: 3030

Using appropriate data structures, you can greatly reduce this code. The idea is to have a data structure that for each year you're interested in, holds an array of ints: one for each month.

Then, converting the substrings from the dates String to numbers, you can use those numbers to index the data structure.

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

private static Map<Integer, int[]> years = new HashMap<>();
private static String[] monthNames = new String[] {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

static {
    for(int year = 16; year <= 18; year++) {
        years.put(year, new int[12]);
    }
}

public static void months(String dates) { // method names should start with lower case
    int year = Integer.parseInt(dates.substring(2, 4));
    int month = Integer.parseInt(dates.substring(5, 7)) - 1; // date String is one-based, array-index is zero-based
    years.get(year)[month]++;
}

public static void print() {
    for(int year = 16; year <= 18; year++) {
        int[] monthCounts = years.get(year);
        for(int month = 0; month < 12; month++) {
            System.out.println(monthNames[month] + " " + year + ": " + monthCounts[month]);
        }
    }
}

You can see the code in action here.

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 338516

YearMonth

Apparently you want to track year-month values. There's a class for that, named, well, YearMonth. Find this class in the java.time package that supplants the terribly troublesome old date-time classes bundled with the earliest versions of Java.

MonthDay

Or maybe you are shooting for month-day values; your Question is convoluted so I am not sure of your goal. But if this is your goal, again, there’s a class for that: MonthDay.

Month

Or maybe you want just the month regardless of year or day-of-month, in which case you can use the Month class.

LocalDate

If your inputs strings represent a year and month and day-of-month, parse as a LocalDate. This class has no time-of-day and no time zone.

LocalDate ld = LocalDate.parse( "2016-01-15" ) ;

Extract a YearMonth, MonthDay, or Month.

YearMonth ym = YearMonth.from( ld ) ;

Create a collection. Perhaps you want to keep all a distinct set of the LocalDate objects in a particular year-month. If so, make a Map where each YearMonth object owns a Set of LocalDate objects.

Map < YearMonth, Set < LocalDate > > map = new HashMap <>();

As you process each input date, check to see if the map has a Set yet created for the particular YearMonth of the input. If not, instantiate a TreeSet. The TreeSet class is a SortedSet, meaning it maintains a sorted order as you add values.

Set < LocalDate > set = map.get( ym );
if ( null == set ) {
    set = new TreeSet <>(); // A `TreeSet` is a `SortedSet`, maintains a sorted order. You may or may not need this behavior.
    map.put( ym , set );
}

With a Set in hand, add your LocalDate.

set.add( ld );

After processing, you can get a collection of the YearMonth keys from your Map. And for each of those, you can retrieve the Set it owns, and get a count of the elements contained.

Lamba & Streams

For shorter code, you might be able to use Lambda syntax & Streams with Map::computeIfAbsent. I've seen this kind of code but have not yet tried it.

map.computeIfAbsent( key , k -> new TreeSet< LocalDate >() ).add( ld ) ;

Count only

If you want only the count, and don't care about the LocalDate values, replace Set as the “value” or you Map with a Integer object. Instead of retrieving the Set and adding to it, retrieve the Integer and increment it by adding one. Personally, in this kind of situation I find it best to collect the LocalDate values to be examined for debugging/testing and/or for further use in other business logic.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 1

mattmess1221
mattmess1221

Reputation: 4434

Basically a mix of what @John T and @Zachary said, but with proper syntax and type conversion.

// [Years] and [Months], where [0][0] is jan 2000. May need to adjust for previous years.
int[][] days = new int[30][12];

void month(String dates) {
    int year = Integer.parseInt(dates.substring(2, 4)); 
    int month = Integer.parseInt(dates.substring(5,7)) - 1;

    days[year][month]++;
}

Upvotes: 3

John T
John T

Reputation: 1082

Loop through your document with this:

// 18 years(more needed?), 12 months
String[][] yearsAndMonths = new String[18][12];  

yearsAndMonths[dates.substring(5,7)][dates.substring(2, 4)]++;

Then print the results.

I'm not a java expert. Code just provided to give you the logic.

Upvotes: -2

Zachary
Zachary

Reputation: 1703

You could use a switch statement to reduce the clunky logic, though this wouldn't necessarily condense greatly. You will either need to use Strings with the Switch or convert the day/month values to an integer.

String day = dates.substring(2, 4);
String month = dates.substring(5, 7);

switch (month) {
    case "01" : {
        if (day.equals("16"))
            jan16++;
        break;
    }
}

If there is some pattern behind what you are wanting to do, there may be a better solution. For example, the following would count 16th of each month

int count[] = new int[12];
  ...
int day = Integer.parseInt(dates.substring(2, 4));
int month = Integer.parseInt(dates.substring(5, 7));


if (day == 16)
    count[month - 1]++;

Upvotes: 1

Related Questions