Reputation: 45
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
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
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.
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 ) ;
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.
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
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
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
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