Search code examples
clojureclojurescriptspecter

How to merge two different collections using specter?


I am trying to write a merge method to generate CSS styles dynamically. This method should take breakpoints, and styles param and creates a map which we use for styling using stylefy.

I am trying to do this using specter, but unable to get desired results.

The code I tried until now:

(defn merge-style
  [breakpoints style]
  (let [media-queries (s/transform [s/ALL] #(hash-map :min-width (str %1 "px")) breakpoints)]
  breakpoints))

The method should work as follows:

(def breakpoints ["320px" "600px" "1280px"])
(def style {:padding-top ["20px" "30px" "40px" "50px"]
            :margin "30px"
           })

(merge-style breakpoints style)

The output should look like the following:

{:padding-top "20px"
 :margin "30px"
 ::stylefy/media {{:min-width "320px"} {:padding-top "30px"}
                  {:min-width "600px"} {:padding-top "40px"}
                  {:min-width "1280px"} {:padding-top "50px"}}
 }

SOLUTION: I solved this problem for myself using the following function

(defn- get-media-queries
  [breakpoints styles]
  (let [base-style (s/transform [s/MAP-VALS] #(%1 0) styles)
        styles-maps (s/setval [s/MAP-VALS empty?] s/NONE (s/setval [s/MAP-VALS s/FIRST] s/NONE styles))
        styles-list (map (fn [[key val]] (map #(hash-map key %1) val)) styles-maps)
        styles-final (apply vdu/merge-maps styles-list)
        breaks (map #(hash-map :min-width %1) breakpoints)
        styles-merged (into {} (mapv vector breaks styles-final))
        ]
    (assoc base-style ::stylefy/media styles-merged)))

Thanks a lot for providing help.


Solution

  • No need for Spectre on this one. Just use basic Clojure:

    (ns tst.demo.core
      (:use demo.core tupelo.core tupelo.test) )
    
    (def desired
      {:padding-top   "20px"
       :margin        "30px"
       :stylefy/media {{:min-width "320px"}  {:padding-top "30px"}
                       {:min-width "600px"}  {:padding-top "40px"}
                       {:min-width "1280px"} {:padding-top "50px"}}})
    
    (def breakpoints [320 600 1280])
    (def padding-top ["30px" "40px" "50px"])
    (def base {:padding-top "20px"
               :margin      "30px"})
    
    (dotest
      (let [mw     (for [it breakpoints]
                     {:min-width (str it "px")})
            pt     (for [it padding-top]
                     {:padding-top it})
            pairs  (zipmap mw pt)
            result (assoc base :stylefy/media pairs)]
        (is= desired result)))
    

    Note that since I don't have a namespace alias for stylefy, I am only using the single-colon version of the keyword.


    Update 2019-9-12

    Was just revisiting this post, and noticed I should have used zipmap instead of zip, even though the previous version (accidentally!) got the right answer.