1

I have 7 boolean variables in a java object for the days of the week (Monday through Sunday). I am trying to find a succinct way from a for loop of a list of days to say the following:

    private boolean isWorkDay(String day, Object pgMasterTaskList) {
        if(day=="Sunday" && pgMasterTaskList.Sunday==true) {
            return true;
        }
        if(day=="Monday" && pgMasterTaskList.Monday==true) {
            return true;
        }
        if(day=="Tuesday" && pgMasterTaskList.Tuesday==true) {
            return true;
        }
        if(day=="Wednesday" && pgMasterTaskList.Wednesday==true) {
            return true;
        }
        if(day=="Thursday" && pgMasterTaskList.Thursday==true) {
            return true;
        }
        if(day=="Friday" && pgMasterTaskList.Friday==true) {
            return true;
        }
        if(day=="Saturday" && pgMasterTaskList.Saturday==true) {
            return true;
        }
        else return false;
    }

I'm hoping I can minimize the repetition with something like...

private boolean isWorkDay(String day, Object pgMasterTaskList) {
        if(pgMasterTaskList.getVariableByName(day)==true) {
            return true;
        }
        else return false;
    }

My thought is to use reflection to check if the value of the variable named "Monday" and so on, but I'm not sure how to use it in such a way.

Edit

I have submitted an answer that works for my specific case. A little more detail...

I'm writing this information out to a pdf so I was handling string comparisons because I have to map them to field names in the pdf. So I was trying to be consistent. ANY combination of days could be a work day. I think this leaves me with 128 combos? (2^7). The information is being fed in by csv so I wanted to read day in as a string since it's coming in as such.

halfer
  • 19,824
  • 17
  • 99
  • 186
MatthewC
  • 41
  • 7
  • 8
    Note: Comparing strings using `==` in Java does not do what you think it does. Use `equals` instead. See: [How do I compare strings in Java?](https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java/513839) – Jesper May 16 '20 at 17:07
  • 1
    You may also consider to refactor to [DayOfWeek](https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html) – maio290 May 16 '20 at 17:10
  • 1
    Use like this `day.equalsIgnoreCase("Sunday")`, it will solve your problem. – Sambit May 16 '20 at 17:10
  • 1
    Instead of 7 `boolean`s use an `EnumSet`. – Ole V.V. May 16 '20 at 17:13
  • is every day a workday? – pero_hero May 16 '20 at 17:17
  • @pero_hero every day 'could be' a work day. Based on the project being worked on, it could be 1-7 days, and could be any combination. 7 factorial combinations as far as I can tell. – MatthewC May 16 '20 at 17:38
  • Is it worth transferring the material in the question, that comments on the answer, to the answer? I am not sure why one would have a question that "knows its own answer" - the point of questions is that the answer is not known yet. – halfer May 16 '20 at 17:45
  • What exactly does a line in your CSV file look like? You can leave out irrelevant fields, of course. Are you using a CSV library or parsing”by hand“? I am not necessarily convinced that we're done inventing suggestions for this problem. – Ole V.V. May 16 '20 at 18:17

5 Answers5

2

If you can modify your task list object to hold a Set of working days rather than 7 Boolean values, you don’t even need a loop. A table lookup in linear time will do.

    DateTimeFormatter dowFormatter = DateTimeFormatter.ofPattern("cccc", Locale.ENGLISH);

    EnumSet<DayOfWeek> workdays = EnumSet
            .of(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.THURSDAY);
    String day = "Tuesday";

    DayOfWeek dow = dowFormatter.parse(day, DayOfWeek::from);
    boolean isWorkDay = workdays.contains(dow);

    System.out.println("Is work day? " + isWorkDay);

Output from the example:

Is work day? true

An EnumSet is implemented as an array of Boolean values.

I trust you to fit the EnumSet into your object and wrap the code in a method similar to what you are doing in the question.

The pattern letter c that I am using in the format pattern string used for defining the formatter for day of week is not often used. Frankly I am in doubt whether it is correct to use it here. According to the documentation it gives us the standalone form of the localized day of week text. Since the day of week standa alone — it is not part of a date string — I figured it might be right. If not, use the more common EEEE. At least for English, both work.

Edit: Even though your 7 boolean values come from a CSV file, I would probably still use the set approach. For initializing the set, I don’t think I’d resort to reflection, but accept 7 if statemnts (here condensed more than most of us do in real code):

    boolean sunday = false;
    boolean monday = true;
    boolean tuesday = true;
    boolean wednesday = false;
    boolean thursday = true;
    boolean friday = false;
    boolean saturday = false;

    Set<DayOfWeek> workdays = EnumSet.noneOf(DayOfWeek.class);
    if (sunday) workdays.add(DayOfWeek.SUNDAY);
    if (monday) workdays.add(DayOfWeek.MONDAY);
    if (tuesday) workdays.add(DayOfWeek.TUESDAY);
    if (wednesday) workdays.add(DayOfWeek.WEDNESDAY);
    if (thursday) workdays.add(DayOfWeek.THURSDAY);
    if (friday) workdays.add(DayOfWeek.FRIDAY);
    if (saturday) workdays.add(DayOfWeek.SATURDAY);

    System.out.println(workdays);

[MONDAY, TUESDAY, THURSDAY]

Edit 2:

Here is a simple example of converting a line of Boolean values from a CSV file to an EnumSet<DayOfWeek>.

    String lineFromCsv = "false;true;true;false;true;false;false";

    String[] workDayStrings = lineFromCsv.split(";");
    Set<DayOfWeek> workdays = EnumSet.noneOf(DayOfWeek.class);
    // Sunday
    if (Boolean.valueOf(workDayStrings[0])) {
        workdays.add(DayOfWeek.SUNDAY);
    }
    // Monday through Saturday
    for (int i = 1; i < workDayStrings.length; i++) {
        if (Boolean.valueOf(workDayStrings[i])) {
            workdays.add(DayOfWeek.of(i));
        }
    }
    System.out.println(workdays);

[MONDAY, TUESDAY, THURSDAY]

Link

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Without me giving further explanation this answer is great. Unfortunately that "workdays" set is coming in as a csv and could change week by week. I have found an answer that worked for me elsewhere but I really appreciate your input! – MatthewC May 16 '20 at 17:35
  • Thanks for reporting back. It’s probably a matter of taste more than anything else. I think I’d still want to stick to the `EnumSet` approach. See my edit. – Ole V.V. May 16 '20 at 17:47
  • @MatthewC I have added a simple example of converting the Boolean values from the CSV file to an `EnumSet` — with a loop (you may alternatively use a stream operation, left to the reader). – Ole V.V. May 17 '20 at 13:14
1

I have found an answer to my question using the following method:

  public static Object getValueOf(Object clazz, String lookingForValue)
      throws Exception {
    Field field = clazz.getClass().getField(lookingForValue);
    Class clazzType = field.getType();
    if (clazzType.toString().equals("double"))
      return field.getDouble(clazz);
    else if (clazzType.toString().equals("int"))
      return field.getInt(clazz);
    // else other type ...
    // and finally
    return field.get(clazz);
  }

I call it as following now:

    private boolean isWorkDay(String day, PlanGridMasterTaskList pgMasterTaskList) throws Exception {
        System.out.println(getValueOf(pgMasterTaskList, day));
        if ((boolean) getValueOf(pgMasterTaskList, day)) {
            return true;
        }
        else
            return false;
    }

I found it at this link: https://www.rgagnon.com/javadetails/java-0038.html

Thank you to everyone who took the time to respond (and so quickly at that!).

MatthewC
  • 41
  • 7
  • Thanks for answering your own question. It’s not completely clear to me how this code fits into the code in the question, though…? – Ole V.V. May 16 '20 at 17:49
  • @OleV.V. Woops! It would be helpful if I make reference to that original method, wouldn't it? Haha. I've made the edit above. – MatthewC May 16 '20 at 17:57
  • 2
    @MatthewC, in above approach, I can suggest that you don't need multiple if-else in getValueOf() method. You can directly return field.get(clazz) and type-cast the returned object where ever you intend to use it. Current code might be not useful as all methods in field viz. getInt, getDouble return an Object which you need to cast at the getValue() calling point. Refer to this line `if ((boolean) getValueOf(pgMasterTaskList, day)) ` where you cast the value to boolean. So, you can remove the multiple if-else and just have field.get(clazz) – AnonymousFox May 16 '20 at 18:05
1

You can use the Object class .getClass().getFields() method to fetch all the variables in a class or use specific .getClass().getField("variableName") to fetch a particular variable.

The java reflection api will allow to fetch the variable as a Field (java.lang.reflect)

  private boolean isWorkDay(String day, Object pgMasterTaskList)
      throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
    Object obj = null;
    if (pgMasterTaskList.getClass().getField(day).get(obj).equals(true)) {
      return true;
    } else
      return false;
  }

The line Object obj = null; in above is just a placeholder to fetch of the variable using .get().

However, this will work only when the value for day parameter exactly matches the variable name as defined in pgMasterTaskList class. If you want a fetch based on case-insensitive scenario you can use the .getFields() method. Below is an example for the same:

  private boolean isWorkDay(String day, Object pgMasterTaskList) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
    Object obj = null;
    for (Field field : pgMasterTaskList.getClass().getFields()) {
      if (day.equalsIgnoreCase(field.getName())) {
        obj = field.get(obj);
        if ((boolean) obj == true)
          return true;
      }
    }
    return false;
  }

The .get(obj) will return an object of class type of your variable (your variable was boolean, then return obj will be of type boolean).

AnonymousFox
  • 118
  • 7
0

You must use reflection for this:

private boolean isWorkDay(String day, Object pgMasterTaskList) throws NoSuchFieldException, NullPointerException, SecurityException {
    return pgMasterTaskList.getClass().getField(day);
}

NOTE: callers must handle the cases where they pass a day that's null or just wrong, or the boolean member aren't public.

Dave
  • 1,579
  • 14
  • 28
0

Try creating an enum and then you method logic would simply look like below,

public class WeekMap {

    public static void main(String[] args) {
        System.out.println(isWorkDay("SUNDAY",WeekDays.SUNDAY));
        System.out.println(isWorkDay("sunday",WeekDays.SUNDAY));
        System.out.println(isWorkDay("sunday",WeekDays.SATURDAY));
        System.out.println(isWorkDay("ABS",WeekDays.SATURDAY));
    }

    private static boolean isWorkDay(String day, WeekDays pgMasterTaskList) {
        WeekDays day1 = WeekDays.valueOf(day.toUpperCase()); // Fetch the mapped day for the string
        try{
            day1 = WeekDays.valueOf(day.toUpperCase());
        } catch (IllegalArgumentException iex) {
            iex.printStackTrace(); // this can be suppress
        }
        return day1 == pgMasterTaskList;
    }
}

enum WeekDays{ // Create enum for week day names
    SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY;
}

QuickSilver
  • 3,915
  • 2
  • 13
  • 29