Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active September 18, 2020 15:14
Show Gist options
  • Save ericnormand/f43768bac0024936e0274cfd2ce06aae to your computer and use it in GitHub Desktop.
Save ericnormand/f43768bac0024936e0274cfd2ce06aae to your computer and use it in GitHub Desktop.
395 PurelyFunctional.tv Newsletter

Seasons

Well, it's Hurricane Season here in the Gulf South. But that's not the kind of seasons I'm talking about now.

Your job is to take a month (keyword) day (number) and a hemisphere (:north or :south) and determine which season it is (return a keyword), according to this handy table.

Start       End         North  South
March 1     May 31      Spring Autumn
June 1      August 31   Summer Winter
September 1 November 30 Autumn Spring
December 1  February 29 Winter Summer

Example:

(which-season :north :march 5) ;=> :spring
(which-season :south :december 25) ;=> :summer

Thanks to this site for the challenge idea where it is considered Hard level in JavaScript.

Please submit your solutions as comments to this gist. Discussion is welcome.

@msladecek
Copy link

msladecek commented Sep 18, 2020

I first started with a nested map with month and then hemisphere for keys.
Then I wanted to extend the solution to also cover astronomical seasons, while keeping the (get-in seasons [date hemisphere]) pattern.

Here it goes

(def month->int (zipmap [:january :february :march :april :may :june :july :august :september :october :november :december]
                        (range)))

(defn date<= [& dates]
  (->> dates
       (map (fn [[month day]] [(month->int month) day]))
       (partition 2 1)
       (map #(apply compare %))
       (every? #(<= % 0))))

(def seasons-astronomical
  (reify clojure.lang.ILookup
    (valAt [this date]
      (cond
        (date<= [:march     21] date [:june      20]) {:north :spring :south :autumn}
        (date<= [:june      21] date [:september 22]) {:north :summer :south :winter}
        (date<= [:september 23] date [:december  20]) {:north :autumn :south :spring}
        (or (date<= [:december 21] date [:december 31])
            (date<= [:january   1] date [:march    20])) {:north :winter :south :summer}))))


(def seasons-meteorological
  (reify clojure.lang.ILookup
    (valAt [this [month _]]
      (let [by-season {#{:march     :april   :may}      {:north :spring :south :autumn}
                       #{:june      :july    :august}   {:north :summer :south :winter}
                       #{:september :october :november} {:north :autumn :south :spring}
                       #{:december  :january :february} {:north :winter :south :summer}}
            by-month (into {}
                           (mapcat
                            (fn [[months by-hemisphere]]
                              (for [month months]
                                [month by-hemisphere]))
                            by-season))]
        (by-month month)))))

(defn which-season
  ([hemisphere month day]
   (get-in seasons-meteorological [[month day] hemisphere]))
  ([seasons hemisphere month day]
   (get-in seasons [[month day] hemisphere])))


(which-season :north :march 5) ;=> :spring
(which-season :south :december 25) ;=> :summer

(which-season seasons-astronomical :north :march 5) ;=> :winter
(which-season seasons-astronomical :south :december 25) ;=> :summer

@sztamas
Copy link

sztamas commented Sep 18, 2020

;; Vector of seasons in order containing the months in each season.
;; Starts with spring in northern hemisphere (autumn in southern hemisphere).
(def seasons [#{:march :april :may}
              #{:june :july :august}
              #{:september :october :november}
              #{:december :january :february}])

;; Can be left out if we don't validate days
(defn max-days-in-month [month]
  (condp contains? month
    #{:february}                         29
    #{:april :june :september :november} 30
    31))

(def season-names {:north [:spring :summer :autumn :winter]
                   :south [:autumn :winter :spring :summer]})

(defn season-index [month]
  (first (keep-indexed #(when (%2 month) %1) seasons)))

(defn which-season
  [hemisphere month day]
  {:pre [(contains? season-names hemisphere)
         (season-index month)
         (<= 1 day (max-days-in-month month))]}
  (let [seasons (hemisphere season-names)]
    (seasons (season-index month))))

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