Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Created May 17, 2021 13:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericnormand/ac5b2ce76ec0dbf354b869579f983488 to your computer and use it in GitHub Desktop.
Save ericnormand/ac5b2ce76ec0dbf354b869579f983488 to your computer and use it in GitHub Desktop.
427 PurelyFunctional.tv Newsletter

Friday the 13ths

For some reason, when the 13th day of the month is a Friday, we consider that unlucky. Because it's such a popular superstition (and even those who don't believe in it enjoy the fun), we're making a website for it. Our job is to write some backed routines that the frontend team will add to pages.

Write the following:

  • A function that tells you the next upcoming Friday the 13th.
  • A function that tells you all the Friday the 13ths in a given year.
  • A predicate that tells you if a given year + month had a Friday the 13th.
  • A predicate to know if a given date (year, month, day) is a Friday the 13th.
  • A function that when the next upcoming Friday the 13th falls in a given month.

If you're working in JVM Clojure, use the Java 8 java.time package.

Thanks to this site for the problem idea, where it is rated Hard in Ruby. The problem has been modified.

Please submit your solutions as comments on this gist.

To subscribe: https://purelyfunctional.tv/newsletter/

@sztamas
Copy link

sztamas commented May 20, 2021

(ns eric-normand-newsletter-challenges.friday-13
  (:import [java.time DayOfWeek LocalDate]))


(defn friday? [y m d]
  (= DayOfWeek/FRIDAY (.getDayOfWeek (LocalDate/of y m d))))

(defn friday-the-13th?
  ([^LocalDate d]
   (friday-the-13th? (.getYear d) (.getMonthValue d) (.getDayOfMonth d)))
  ([y m d]
   (and (= d 13) (friday? y m d))))

(defn has-friday-the-13th? [y m]
  (friday-the-13th? y m 13))

(defn future-friday-the-13ths
  ([] (future-friday-the-13ths (LocalDate/now)))
  ([start-date]
   (let [first-f13 (.withDayOfMonth start-date 13)]
     (->> (range)
          (map #(.plusMonths first-f13 %))
          (drop-while #(.isBefore % start-date))
          (filter friday-the-13th?)))))

(def next-friday-the-13th (comp str first future-friday-the-13ths))

(defn all-friday-the-13ths [y]
  (->> (LocalDate/of y 1 1)
       future-friday-the-13ths
       (take-while #(= y (.getYear %)))
       (map str)))


(comment
  (friday? 2021 8 7) ;; => false
  (friday? 2021 8 6) ;; => true

  (friday-the-13th? 2021 7 13) ;; => false
  (friday-the-13th? 2021 8 13) ;; => true

  (has-friday-the-13th? 2021 7) ;; => false
  (has-friday-the-13th? 2021 8) ;; => true

  (all-friday-the-13ths 2020) ;; => ("2020-03-13" "2020-11-13")
  (all-friday-the-13ths 2021) ;; => ("2021-08-13")

  (take 5 (map str (future-friday-the-13ths)))
  ;; => ("2021-08-13" "2022-05-13" "2023-01-13" "2023-10-13" "2024-09-13")
  (take 5 (map str (future-friday-the-13ths (LocalDate/of 2000 1 1))))
  ;; => ("2000-10-13" "2001-04-13" "2001-07-13" "2002-09-13" "2002-12-13")

  (next-friday-the-13th) ;; => "2021-08-13"
  (next-friday-the-13th (LocalDate/of 2021 8 14)) ;; => "2022-05-13"

  )

@KingCode
Copy link

KingCode commented May 22, 2021

;; utilities, with JVM's java.time interop at the bottom
(defn new-date 
  ([y m d]
   (java.time.LocalDate/of y m d)))

(defn now []
  (java.time.LocalDate/now))

(def Friday java.time.DayOfWeek/FRIDAY)

(defn year-of [^java.time.LocalDate ld]
  (.getYear ld))

(defn month-of [^java.time.LocalDate ld]
  (.getMonthValue ld))

(defn days-in-month [^java.time.LocalDate ld]
  (.lengthOfMonth ld))

(defn day-of [^java.time.LocalDate ld]
  (.getDayOfMonth ld))

(defn as-day-13th [^java.time.LocalDate ld]
  (.withDayOfMonth ld 13))

(defn weekday-of [^java.time.LocalDate ld]
  (.getDayOfWeek ld))

(defn n-months-after [^long n ^java.time.LocalDate ld]
  (.plusMonths ld n))

(defn n-days-after [^long n ^java.time.LocalDate ld]
  (.plusDays ld n))

(defn n-years-after [^long n ^java.time.LocalDate ld]
  (.plusYears ld n))

(defn inc-year [ld]
  (n-years-after 1 ld))

(defn inc-month [ld]
  (n-months-after 1 ld))

(defn inc-day [ld]
  (n-days-after 1 ld))

(defn ->ymd [ld]
  (-> ld ((juxt year-of month-of day-of))))

(defn friday? [ld]
  (= Friday (weekday-of ld)))

(defn the-13th? [ld]
  (= 13 (day-of ld)))

(defn forward-units [from to period]
  (let [diff (- to from)]
    (if (<= 0 diff)
      diff
      (-> period (- from) (+ to)))))

(defn forward-to [to ld period rdr forwardr]
  (let [now (or ld (now))
        from (rdr now)
        dist (forward-units from to period)]
    (forwardr dist now)))

(defn next-date-having-month [m]
  (forward-to m nil 12 month-of n-months-after ))

(defn next-date-having-day13 [from-ld]
  (forward-to 13 from-ld (days-in-month from-ld) day-of n-days-after))

;;; user functions 
(defn friday-the-13th? 
  "A predicate to know if a given date (year, month, day) is a Friday the 13th."
  ([y m d]
   (friday-the-13th? (new-date y m d)))
  ([ld]
   (and (the-13th? ld) (friday? ld))))

(defn next-friday-the-13th 
  "A function that tells you the next upcoming Friday the 13th."
  ([]
   (next-friday-the-13th (now)))
  ([ld]
   (->> ld next-date-having-day13
        (iterate inc-month)
        (filter friday?) first
        ->ymd)))

(defn friday-the-13ths-for-year 
"A function that tells you all the Friday the 13ths in a given year."
  [yr]
  (->> (range 1 13)
       (map #(new-date yr % 13))
       (filter friday?)
       (map ->ymd)))

(defn next-friday-the-13th-having-month
"A function that when the next upcoming Friday the 13th falls in a given month."
 [m]
  (->> m next-date-having-month as-day-13th
       (iterate inc-year)
       (some (fn [ld]
                 (when (friday? ld)
                   ld)))
       ->ymd))

(->ymd (now))  
;;=> [2021 5 22]
(next-friday-the-13th)
;;=> [2021 8 13]
(friday-the-13ths-for-year 2020)
;;=> ([2020 3 13] [2020 11 13])
(next-friday-the-13th-having-month 1) ;; when is the next Friday-13th in January?
;;=> [2023 1 13]

;; Trivia time: 
;; - What is the most frightening count of f-13ths in any Gregorian calendar year so far?
;; - How many years have had it? 
;; - Which are the earliest/latest years?
;; - Which are the N most recent scariest years and their distribution of fright? 
(defn scariest-years-trivia [n-most-recent]
  (let [[scary-count scary-years]
        (->> (range 1582 2022)
             (map friday-the-13ths-for-year)
             (reduce (fn [[max-count f13-yrs] f13yr]
                       (if (<= max-count (count f13yr))
                         [(count f13yr), (conj f13-yrs f13yr)]
                         [max-count f13-yrs]))
                     [0 ()]))]
    (->> scary-years
         (filter #(= scary-count (count %)))
         (take n-most-recent)
         (sorted-map :1_How-many-scariest-years-so-far? 
                     (count scary-years)
                     :2_What-is-the-max-count-of-scary-fridays-in-any-year? 
                     scary-count 
                     :3_What-are-the-earliest-and-latest-scariest-years? 
                     (->> [(last scary-years), (first scary-years)]
                          (mapv ffirst))
                     (keyword :4_Which-are-the-friday13ths-of-the-most-recent-scariest-years?))))) 

(scariest-years-trivia 2)
;;=>
;; {:1_How-many-scariest-years-so-far? 67,
;;; :2_What-is-the-max-count-of-scary-fridays-in-any-year? 3,
;;; :3_What-are-the-earliest-and-latest-scariest-years? [1582 2015],
;;; :4_Which-are-the-friday13ths-of-the-most-recent-scariest-years?
;;; (([2015 2 13] [2015 3 13] [2015 11 13])
;;;; ([2012 1 13] [2012 4 13] [2012 7 13]))}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment