0
\$\begingroup\$

The code below works, but it seems it could be done better. Is there a better/more efficient way to do this?

Given:

  1. All days off ("relief days") will be consecutive.
  2. Relief days will be imported as a string. Days of week are represented by their first letter. "S" can represent Saturday or Sunday, "T" can represent Tuesday or Thursday. I.e., someone with Sat/Sun off would be "SS", someone with Tue/Wed/Thu off would be "TWT".
  3. Depending on how many hours a day they work, they'll either have 2, 3, or 4 days off.

The existing code:

    public static String getReliefDays(){

        Map<String, String> user = new HashMap<>(); //test example user
        user.put("name", "Schmoe, Joe");
        user.put("employeeNo", "123456");
        user.put("startTime", "0800");
        user.put("endTime", "1700");
        user.put("reliefDays", "TF");

        String rd = (String) user.get("reliefDays");
        int rdLength = rd.length();
        String reliefDays = "";
        String fl = rd.substring(0, 1); //first letter in the days off string
        String sl = rd.substring(1, 2); //second letter in the days off string

        switch (fl) {
            case "S":
                if (sl.equalsIgnoreCase("M")) {
                    reliefDays = "SUN MON";
                if (rdLength == 3) {
                    reliefDays += " TUE";
                }
                if (rdLength == 4) {
                    reliefDays += " WED";
                }
            }else{
                reliefDays = "SAT SUN";
                if (rdLength == 3) {
                    reliefDays += " MON";
                }
                if (rdLength == 4) {
                    reliefDays += " TUE";
                }                    
            }
            break;
        case "M":
            reliefDays = "MON TUE";
            if (rdLength == 3) {
                reliefDays += " WED";
            }
            if (rdLength == 4) {
                reliefDays += " THU";
            }
            break;
        case "T":
            if (sl.equalsIgnoreCase("W")) {
                reliefDays = "TUE WED";
                if (rdLength == 3) {
                    reliefDays += " THU";
                }
                if (rdLength == 4) {
                    reliefDays += " FRI";
                }
            } else {
                reliefDays = "THU FRI";
                if (rdLength == 3) {
                    reliefDays += " SAT";
                }
                if (rdLength == 4) {
                    reliefDays += " SUN";
                }
            }
            break;
        case "W":
            reliefDays = "WED THU";
            if (rdLength == 3) {
                reliefDays += " FRI";
            }
            if (rdLength == 4) {
                reliefDays += " SAT";
            }
            break;
        case "F":
            reliefDays = "FRI SAT";
            if (rdLength == 3) {
                reliefDays += "SUN";
            }
            if (rdLength == 4) {
                reliefDays += " MON";
            }
            break;
        }
        return reliefDays;
    }
\$\endgroup\$
6
  • \$\begingroup\$ Do you have a method header for this? It's best to show complete, runnable code, perhaps with a few test cases in a main to help clarify the behavior at a glance -- I'm not sure what format String rd is or what the output should be for different inputs. How are you supposed to differentate between Tu/Th and Sa/Su if rd is single-letter abbreviations? Thanks. \$\endgroup\$
    – ggorlen
    Jul 28, 2021 at 16:40
  • \$\begingroup\$ @ggorlen From the conditions, rd will never be a single character and will be consecutive days. I'm kind of at the mercy of the data I'm being sent, and SMTWTFS is the format that I'm sent for days of the week. I've updated the question to make the code snippet a method. \$\endgroup\$
    – Bob Stout
    Jul 28, 2021 at 17:38
  • \$\begingroup\$ This still doesn't fit site standards. Obviously you would never use this method for anything. It has its test data hard coded inside it. You claim that the code "works" but it's obvious that it doesn't do anything. What we would want to see is how the code would actually be called (apparently not written yet) and how the results would be used (apparently not written yet). As written, a better way to write the current code would be return "SAT SUN"; which seems rather useless (not to mention probably wrong, as "THU FRI" would seem a more likely result). \$\endgroup\$
    – mdfst13
    Jul 28, 2021 at 18:58
  • \$\begingroup\$ @mdfst13: I saw the error I had for Thursday. Valid point, good catch, thank you. As for the rest of your comment, I was looking for a more efficient or condensed way to rewrite the ~80 lines of code in a project with ~8000 lines of code. I can add the code that generates the HTML that the function is used in and the servlet that delivers said html and the database code that pulls data from three disparate databases, but that seemed excessive...so I just put the function that I needed help with on here. \$\endgroup\$
    – Bob Stout
    Jul 28, 2021 at 19:28
  • \$\begingroup\$ But this isn't the actual function (called methods in Java). It's a modified version with test code added. For what you are attempting, the thing to do would be to write unit tests for the code. That would both help (because unit tests verify functionality in the face of changes) and make it easier for us to understand what you are doing. But a larger problem here is that both the input and output representations are not best practices. In particular, why do you want output like "SAT SUN MON TUE"? As opposed to [DaysOfWeek.SATURDAY, 4] or something else. \$\endgroup\$
    – mdfst13
    Jul 28, 2021 at 21:29

1 Answer 1

2
\$\begingroup\$

Your indentation has gone wrong here, and it's confusing:

             if (sl.equalsIgnoreCase("M")) {
                 reliefDays = "SUN MON";
             if (rdLength == 3) {
                 reliefDays += " TUE";
             }
             if (rdLength == 4) {
                 reliefDays += " WED";
             }
         }

Let your IDE help and auto-format your code as you work.

Other than that, you can get better feedback when you post not only the code you want feedback on but also the surrounding context, so that you can get feedback on how you are applying OOP and using the correct signatures.

My version is below, see how I've divided up the logic.

I figured that this job of creating a String with the weekday short names would belong to the User so I put it there.

I also thought that a Weekday class might know how to deal with the problem of disambiguating weekdays as single letters in a String that represents week days. But I can also see the argument for this to belong to User since that sort of String is known to be specifically used when creating Users. In the end, I decided that Weekday would expose the logic, and User will expose setReliefDays taking a String, which would delegate to the logic in Weekday, but then this can easily be changed if the implementation of User needs to change.

public class ReliefDays {

    public static void main(String[] args) {
        User joe = new User();
        joe.name = "Schmoe, Joe";
        joe.employeeNo = "123456";
        joe.startTime = "0800";
        joe.endTime = "1700";
        joe.setReliefDays("TFSS");
        System.out.println(joe.reliefDaysShortNames());
    }

    static class User {
        String name;
        String employeeNo;
        String startTime;
        String endTime;
        Weekday[] reliefDays;

        /**
         * Returns a string containing the short names of this user's relief days
         * seperated by spaces.
         * 
         * For example, "SAT SUN MON".
         */
        public String reliefDaysShortNames() {
            StringBuilder shortDays = new StringBuilder();
            for (int i = 0; i < reliefDays.length - 1; i++) {
                shortDays.append(reliefDays[i].shortName);
                shortDays.append(" ");
            }
            shortDays.append(reliefDays[reliefDays.length - 1].shortName);
            return shortDays.toString();
        }

        /**
         * Set this user's relief days based on a string of characters representing
         * consecutive days.
         * 
         * For example, when days is "MT", this user's relief days are set to
         * {Weekday.MONDAY, Weekday.TUESDAY}.
         */
        public void setReliefDays(String days) {
            reliefDays = Weekday.parseWeekdays(days);
        }
    }

    static enum Weekday {
        MONDAY('M', "MON"),
        TUESDAY('T', "TUE"),
        WEDNESDAY('W', "WED"),
        THURSDAY('T', "THU"),
        FRIDAY('F', "FRI"),
        SATURDAY('S', "SAT"),
        SUNDAY('S', "SUN");

        char letter;
        String shortName;

        Weekday(char letter, String shortName) {
            this.letter = letter;
            this.shortName = shortName;
        }

        /**
         * Given at least two characters representing consecutive week days, returns an
         * array of those weekdays of the same size and in the same order.
         */
        public static Weekday[] parseWeekdays(String s) {
            Weekday[] weekDays = new Weekday[s.length()];
            Weekday currentDay = firstWeekdayOfPair(s.substring(0, 2));
            Weekday nextDay;

            weekDays[0] = currentDay;
            for (int i = 1; i < s.length(); i++) {
                nextDay = currentDay.nextWeekday();
                if (s.charAt(i) != nextDay.letter) {
                    throw new IllegalArgumentException("Unexpected sequence of days: "
                            + currentDay.letter + s.charAt(i));
                }
                weekDays[i] = nextDay;
                currentDay = nextDay;
            }
            return weekDays;
        }

        private static Weekday firstWeekdayOfPair(String s) {
            switch (s) {
            case "MT":
                return MONDAY;
            case "TW":
                return TUESDAY;
            case "WT":
                return WEDNESDAY;
            case "TF":
                return THURSDAY;
            case "FS":
                return FRIDAY;
            case "SS":
                return SATURDAY;
            case "SM":
                return SUNDAY;
            default:
                throw new IllegalArgumentException("Unexpected weekday pair: " + s);
            }
        }

        private Weekday nextWeekday() {
            switch (this) {
            case MONDAY:
                return TUESDAY;
            case TUESDAY:
                return WEDNESDAY;
            case WEDNESDAY:
                return THURSDAY;
            case THURSDAY:
                return FRIDAY;
            case FRIDAY:
                return SATURDAY;
            case SATURDAY:
                return SUNDAY;
            case SUNDAY:
                return MONDAY;
            default:
                throw new IllegalArgumentException("Unexpected day: " + this.toString());
            }
        }

    }

}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Nice solution. One remark is that nextWeekday can be replaced by playing with the ordinal value of the enum. Something like; values[this.ordinal()>values.length?0:this.ordinal()+1] \$\endgroup\$
    – gervais.b
    Jul 29, 2021 at 11:21
  • \$\begingroup\$ THIS is what I was looking for. Much thanks! \$\endgroup\$
    – Bob Stout
    Jul 29, 2021 at 12:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.