Idiomatic clojure map lookup by keyword

后端 未结 4 882
忘了有多久
忘了有多久 2020-11-29 08:09

Say I have a clojure map that uses keywords as its keys:

(def my-car {:color \"candy-apple red\" :horsepower 450})

I know that I can look u

4条回答
  •  误落风尘
    2020-11-29 08:36

    I put together a list of arguments for and against the two forms. (Edit: Added third option - (get map :key) which is my new favorite despite being a little bit more verbose)

    Arguments for (:key map)

    1) Requested in coding standards

    http://dev.clojure.org/display/community/Library+Coding+Standards

    2) Still works when map is nil

    > (:a nil)
      nil
    > (nil :a)
      ERROR: can't call nil
    

    ---counterargument--- if key may be nil, other forms are better

    > ({:a "b"} nil)
      nil
    > (nil {:a "b"})
      ERROR: can't call nil
    

    3) Works better for threading and mapping over collections of objects

    (-> my-map
      :alpha
      fn-on-alpha
      :beta
      fn-on-beta
      :gamma
    
    > (def map-collection '({:key "values"} {:key "in"} {:key "collection"}))
    > (map :key map-collection)
      ("values" "in" "collection")
    

    ---counterargument--- the code structure of threading is different than usual so different idiomatic tendencies could be applied for map access when needed

    4) Potential optimization benefit? (needs verification)

    Arguments for (map :key)

    1) Does not throw error when key is non-keyword or nil

    > ({:a "b"} nil)
      nil
    > (nil {:a "b"})
      ERROR: can't call nil
    > ({"a" "b"} "a")
      "b"
    > ("a" {"a" "b"})
      ERROR: string cannot be cast to IFn
    

    2) Consistency with list access in Clojure

    > ([:a :b :c] 1)
      :b
    > (1 [:a :b :c])
      ERROR: long cannot be cast to IFn
    

    3) Similarity to other forms of object access

    java>         my_obj  .alpha  .beta  .gamma  .delta
    clj >     ((((my-map  :alpha) :beta) :gamma) :delta)
    clj > (get-in my-map [:alpha  :beta  :gamma  :delta])
    cljs> (aget   js-obj  "alpha" "beta" "gamma" "delta")
    

    4) Alignment when accessesing multiple keys from the same map (separate lines)

    > (my-func
        (my-map :un)
        (my-map :deux)
        (my-map :trois)
        (my-map :quatre)
        (my-map :cinq))
    > (my-func
        (:un my-map)
        (:deux my-map)
        (:trois my-map)
        (:quatre my-map)
        (:cinq my-map))
    

    ---counterargument--- alignment worse when accessing same key from multiple maps

    > (my-func
        (:key map-un)
        (:key map-deux)
        (:key map-trois)
        (:key map-quatre)
        (:key map-cinq)
    > (my-func
        (map-un :key)
        (map-deux :key)
        (map-trois :key)
        (map-quatre :key)
        (map-cinq :key)
    

    Arguments for (get map :key)

    1) NEVER causes error if arg1 is map/vector/nil and arg2 is key/index/nil

    > (get nil :a)
      nil
    > (get nil nil)
      nil
    > (get {:a "b"} nil)
      nil
    > (get {:a "b"} :q)
      nil
    > (get [:a :b :c] nil)
      nil
    > (get [:a :b :c] 5)
      nil
    

    2) Consistency in form with other Clojure functions

    > (get {:a "b"} :a)
      :b
    > (contains? {:a "b"} :a)
      true
    > (nth [:a :b :c] 1)
      :b
    > (conj [:a :b] :c)
      [:a :b :c]
    

    3) Alignment benefits of map-first

    > (my-func
        (get my-map :un)
        (get my-map :deux)
        (get my-map :trois)
        (get my-map :quatre)
        (get my-map :cinq))
    

    4) Get-in can be used for nested access with a single call

    > (get-in my-map [:alpha  :beta  :gamma  :delta])
    > (aget   js-obj  "alpha" "beta" "gamma" "delta")
    

    Source: testing on http://tryclj.com/

提交回复
热议问题