如何从 API 响应字符串中获取一周内餐厅营业日列表

How to get list of restaurant operating days with time in a week from the API response string

正在从 API 获取响应,如下所示

{
  "name": "Restaurant name",
  "operatingHours": "Mon-Thu, Sun 11:30 am - 10:00 pm  \/ Fri-Sat 11:30 am - 10:30 pm"
},
{
  "name": "Restaurant name",
  "operatingHours": "Mon-Sat 11:00 am - 11:00 pm  \/ Sun 11:00 am - 10:30 pm"
}

输出列表应该如下所示

Mon 11:30 am - 10:00 pm,
Tue 11:30 am - 10:00 pm,
Wed 11:30 am - 10:00 pm,
Thu 11:30 am - 10:00 pm,
Fri 11:30 am - 10:30 pm,
Sat 11:30 am - 10:30 pm,
Sun 11:30 am - 10:00 pm

以下是我如何设法获得预期的输出。

private fun getRestaurantTimings(operatingHours: String) : List<String> {
val operatingDaysList : MutableList<String> = mutableListOf()
val daysToNo: HashMap<String, Int> = HashMap(7)
daysToNo.put("Mon", 1)
daysToNo.put("Tue", 2)
daysToNo.put("Wed", 3)
daysToNo.put("Thu", 4)
daysToNo.put("Fri", 5)
daysToNo.put("Sat", 6)
daysToNo.put("Sun", 7)

val splitOperationWeeks = operatingHours.split(" \", "/ ").toMutableList()
val restaurantOperationWeeks: MutableList<String> = ArrayList()

for (operatingInWeek in splitOperationWeeks) {
  if (operatingInWeek.contains(", ")) {
    val splitString1 = operatingInWeek.split(", ").toMutableList()
    val splitString2 = splitString1[1].split(" ", limit = 2).toMutableList()
    restaurantOperationWeeks.add(splitString1[1])
    restaurantOperationWeeks.add(splitString1[0] + " " + splitString2[1])
  } else
    restaurantOperationWeeks.add(operatingInWeek)
}

for (restaurantOperationWeek in restaurantOperationWeeks) {
  // splits "Mon-Fri 11:00 am - 9:00 pm” to "Mon" "Fri 11:00 am - 9:00 pm”
  var splitForStartDay = restaurantOperationWeek.split("-", limit = 2).toMutableList()

  //handling of single day open scenario
  if (!restaurantOperationWeek.toCharArray()[3].equals('-')) {
    // splits "Fri 11:00 am - 9:00 pm” to "Fri" "11:00 am - 9:00 pm”
    splitForStartDay = restaurantOperationWeek.split(" ", limit = 2) as MutableList<String>
    //Set end day as Fri to "11:00 am - 9:00 pm"
    splitForStartDay[1] = splitForStartDay[0] + " " + splitForStartDay[1]
  }
  // splits "Fri 11:00 am - 9:00 pm” to "Fri" "11:00 am - 9:00 pm”
  val splitForEndDay = splitForStartDay[1].split(" ", limit = 2)

  val startDateInNo = daysToNo[splitForStartDay[0]]
  val endDateInNo = daysToNo[splitForEndDay[0]]

  for (dayNo in startDateInNo!!..endDateInNo!!) {
    val day = daysToNo.filterValues { it == dayNo }.keys
    operatingDaysList.add(day.toString() + " " + splitForEndDay[1])
  }
}
return operatingDaysList
}

有没有人有更好的解决方案?

假设 operatingHours 始终采用以下格式之一:

  1. Mon-Thu, Sun 11:30 am - 10:00 pm \/ Fri-Sat 11:30 am - 10:30 pm
  2. Mon-Sat 11:00 am - 11:00 pm \/ Sun 11:00 am - 10:30 pm

下面给出了一个替代解决方案:

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        // Test
        String operatingHours = "Mon-Thu, Sun 11:30 am - 10:00 pm \/ Fri-Sat 11:30 am - 10:30 pm";
        System.out.println(getOpenDaysWithTime(operatingHours));

        System.out.println();

        operatingHours = "Mon-Sat 11:00 am - 11:00 pm  \/ Sun 11:00 am - 10:30 pm";
        System.out.println(getOpenDaysWithTime(operatingHours));
    }

    static String getOpenDaysWithTime(String operatingHours) {
        String[] daysArr = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
        List<String> daysList = Arrays.asList(daysArr);
        try {
            // Split the parameter string
            String[] arr = operatingHours.split("\\/");

            // Regex to find the time range e.g. 11:30 am - 10:00 pm
            String durationRegex = "\d+:\d+ (?i)(?:[ap]m) - \d+:\d+ (?i)(?:[ap]m)";

            Pattern pattern = Pattern.compile(durationRegex);
            for (String e : arr) {
                Matcher matcher = pattern.matcher(e);
                if (matcher.find()) {
                    String durationStr = matcher.group();
                    if (durationStr != null) {
                        // String before the time range e.g. Mon-Thu, Sun -OR- Fri-Sat -OR- Sun
                        String daysStr = e.substring(0, e.indexOf(durationStr)).trim();

                        String[] parts = daysStr.trim().split(", ");

                        // Split the first element e.g. Mon-Thu -OR- Fri-Sat
                        String[] days = parts[0].split("-");

                        if (days.length > 1) {
                            for (int i = daysList.indexOf(days[0]); i <= daysList.indexOf(days[1]); i++) {
                                daysArr[i] = daysList.get(i) + " " + durationStr;
                            }
                        } else {
                            daysArr[daysList.indexOf(parts[0])] = parts[0] + " " + durationStr;
                        }

                        if (parts.length == 2) {
                            daysArr[daysList.indexOf(parts[1])] = parts[1] + " " + durationStr;
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Error occured while processing: " + operatingHours);
            daysArr = new String[] {};
        }
        return String.join("," + System.lineSeparator(), daysArr);
    }
}

输出:

Mon 11:30 am - 10:00 pm,
Tue 11:30 am - 10:00 pm,
Wed 11:30 am - 10:00 pm,
Thu 11:30 am - 10:00 pm,
Fri 11:30 am - 10:30 pm,
Sat 11:30 am - 10:30 pm,
Sun 11:30 am - 10:00 pm

Mon 11:00 am - 11:00 pm,
Tue 11:00 am - 11:00 pm,
Wed 11:00 am - 11:00 pm,
Thu 11:00 am - 11:00 pm,
Fri 11:00 am - 11:00 pm,
Sat 11:00 am - 11:00 pm,
Sun 11:00 am - 10:30 pm

Here 是用于查找时间范围的正则表达式的演示。

正则表达式的解释:

  1. \d+ 匹配一位或多位数字

  2. : 按字面意思匹配字符 :

  3. (?i) 指定不区分大小写的匹配

  4. 非捕获组(?:[ap]m)

    • [ap] 匹配 ap
    • 中的单个字符
    • m 按字面意思匹配字符 m
  5. - 按字面意思匹配字符 -

有点晚了,但这是我的 OOP 解决方案:

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Week {
    private static final String[] names = {
            "Mon",
            "Tue",
            "Wed",
            "Thu",
            "Fri",
            "Sat",
            "Sun"
    };

    public static final Week EMPTY = new Week();

    public static final int MONDAY    = 0;
    public static final int TUESDAY   = 1;
    public static final int WEDNESDAY = 2;
    public static final int THURSDAY  = 3;
    public static final int FRIDAY    = 4;
    public static final int SATURDAY  = 5;
    public static final int SUNDAY    = 6;


    final private Day[] week = new Day[7];

    public static Week parse(String table) {

        Week     week   = EMPTY;
        String[] groups = table.split("\\/");
        for (String group : groups) {
            String   daysOnly   = getDays(group);
            String   days       = correctDays(daysOnly);
            String[] filledDays = days.split(",");
            String[] times      = getTime(group);
            for (String filledDay : filledDays) {
                int correctDay = findDayOfWeek(filledDay);
                week.week[correctDay] = new Day.Builder()
                        .dayOfTheWeek(names[correctDay])
                        .timeStart(times[0])
                        .timeEnd(times[1])
                        .build();
            }

        }
        return week;
    }

    private static String getDays(String days) {
        StringBuilder stringBuilder = new StringBuilder();
        for (char ch : days.toCharArray()) {
            if (!Character.isDigit(ch)) {
                stringBuilder.append(ch);
            } else {
                break;
            }
        }
        return stringBuilder.toString().trim();
    }


    private static String correctDays(String days) {
        if (!days.contains("-")) {
            return days;
        }

        final String  regex   = "\w{3}-\w{3}";
        final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        final Matcher matcher = pattern.matcher(days);
        String        result  = "";
        while (matcher.find()) {
            for (int i = 0; i <= matcher.groupCount(); i++) {
                String[]      a             = matcher.group(i).split("-");
                int           start         = findDayOfWeek(a[0]);
                int           end           = findDayOfWeek(a[1]);
                StringBuilder stringBuilder = new StringBuilder();
                for (int j = start; j <= end; j++) {
                    if (j == end) {
                        stringBuilder.append(names[j]);
                        stringBuilder.append(days.replaceAll(matcher.group(i), ""));
                    } else {
                        stringBuilder.append(names[j]).append(", ");
                    }
                }
                result += stringBuilder;
            }
        }
        return result;
    }

    private static String[] getTime(String string) {
        final String      regex   = "\d{2}:\d{2}\s\w{2}";
        final Pattern     pattern = Pattern.compile(regex, Pattern.MULTILINE);
        final Matcher     matcher = pattern.matcher(string);
        ArrayList<String> result  = new ArrayList<>();

        while (matcher.find()) {
            for (int i = 0; i <= matcher.groupCount(); i++) {
                result.add(matcher.group(i));
            }
        }
        String[] array = new String[result.size()];
        return result.toArray(array);
    }

    private static int findDayOfWeek(String day) {
        String comparable = day.trim();
        for (int i = 0; i < names.length; i++) {
            if (comparable.equals(names[i])) {
                return i;
            }
        }
        return -1;
    }

    private Week() {
        for (int i = 0; i < names.length; i++) {
            week[i] = new Day.Builder().dayOfTheWeek(names[i]).build();
        }
    }

    public Day getDay(int day) {
        if (day < 0 || day > 6) {
            throw new IllegalArgumentException("Day value is not correct!");
        }
        return week[day];
    }

    public String getWeekAsString() {
        StringBuilder builder = new StringBuilder();
        for (Day day : week) {
            builder.append(day.dayOfTheWeek)
                    .append(" ")
                    .append(day.getTime(Day.START))
                    .append(" - ")
                    .append(day.getTime(Day.END))
                    .append(",")
                    .append("\n");
        }
        int last = builder.lastIndexOf(",");
        builder.replace(last, last + 1, "");
        return builder.toString();
    }

    static class Day {
        private static final int START = 506;
        private static final int END   = 132;
        String dayOfTheWeek;
        long   timeStart = -1;
        long   timeEnd   = -1;

        public Day() {

        }

        public Day(String dayOfTheWeek, long timeStart, long timeEnd) {
            this.dayOfTheWeek = dayOfTheWeek;
            this.timeStart = timeStart;
            this.timeEnd = timeEnd;
        }

        public String getTime(int type) {
            return switch (type) {
                case START -> stringifyTime(timeStart);
                case END -> stringifyTime(timeEnd);
                default -> throw new IllegalArgumentException("Doesn't match any of Day.START or Day.END!");
            };
        }

        private String stringifyTime(long time) {
            StringBuilder stringBuilder = new StringBuilder();
            int           hours         = (int) time / 1000 / 60 / 60;
            int           minutes       = (int) (time - (hours * 60 * 60 * 1000)) / 1000 / 60;
            final long    twelweHours   = 43200000;
            String sHours = hours < 10 ? "0" + hours : "" + hours;
            String sMinutes = minutes < 10 ? "0" + minutes : "" + minutes;
            if (time - twelweHours < 0) {
                stringBuilder.append(sHours).append(":").append(sMinutes).append(" am");
            } else {
                hours -= 12;
                sHours = hours < 10 ? "0" + hours : "" + hours;
                stringBuilder.append(sHours).append(":").append(sMinutes).append(" pm");
            }
            return stringBuilder.toString();
        }

        public static class Builder {
            private final Day day;

            public Builder() {
                day = new Day();
            }

            public Builder dayOfTheWeek(String dayOf) {
                day.dayOfTheWeek = dayOf;
                return this;
            }

            public Builder timeStart(long start) {
                day.timeStart = start;
                return this;
            }

            public Builder timeEnd(long end) {
                day.timeEnd = end;
                return this;
            }

            public Builder timeStart(String time) {
                day.timeStart = parseTime(time);
                return this;
            }

            public Builder timeEnd(String time) {
                day.timeEnd = parseTime(time);
                return this;
            }

            private long parseTime(String s) {
                long   result  = 0;
                char[] chars   = s.toCharArray();
                int    hour    = 10 * Character.digit(chars[0], 10) + Character.digit(chars[1], 10);
                int    minutes = 10 * Character.digit(chars[3], 10) + Character.digit(chars[4], 10);
                result = (hour * 60L + minutes) * 60 * 1000;
                if (s.contains("pm")) {
                    result += 12 * 60 * 60 * 1000;
                }
                return result;
            }


            public Day build() {
                return day;
            }
        }
    }
}

用法很简单:

final String text = "Mon-Sat 11:00 am - 11:00 pm  \/ Sun 11:00 am - 10:30 pm";
Week week = Week.parse(text);
System.out.println(week.getWeekAsString());