0
votes

I have a vector define the columns in a table as below

(def column ["col1" "col2" "col3" "col4"])

And then several vectors define the type and constraint of the columns.

(def maxInclude [nil nil 1000 nil])
(def minInlcude [nil 200 300  nil])
(def columntype [int float int int])

Then I need to get a map as the following: one map entry defines each column, key is the column name, and value is the type and constraint for that column.

{:col1 {:columntype "int"},
:col2 {:columntype "float" :minInlcude 200},
:col3 {:columntype "float" :minInlcude 300 :maxInclude 1000},
:col4 {:columntype "int" }
}

How to do that?

2

2 Answers

1
votes

I would recommend that you define columntype as a vector of Symbols rather than functions, so:

(def columntype ['int 'float 'int 'int])

instead of:

(def columntype [int float int int])

Also note that minInlcude is misspelled and that I have renamed it to minInclude. You can create the outcome using the following (minInclude has been renamed and columntype is a vector of Symbols):

(def column ["col1" "col2" "col3" "col4"])
(def maxInclude [nil nil 1000 nil])
(def minInclude [nil 200 300  nil])
(def columntype ['int 'float 'int 'int])

(defn remove-nil-vals [m]
  (into {} (map (fn [e]
                  (vector (key e)
                          (into {} (remove (comp nil? val) (val e)))))
             m)))

(remove-nil-vals
 (reduce merge
         (map #(hash-map (keyword %1) {:columntype (str %2)
                                       :minInclude %3
                                       :maxInclude %4})
              column columntype minInclude maxInclude)))


{:col4 {:columntype "int"},
 :col3 {:columntype "int", :minInclude 300, :maxInclude 1000},
 :col2 {:columntype "float", :minInclude 200},
 :col1 {:columntype "int"}}

Clojure naming conventions for Symbols are hyphen separated rather than CamelCase. You should therefore consider using, for example, max-include instead of maxInclude.

1
votes

One way to go about this is to map over all your vectors concurrently. However, you're requiring specific names for your map keys (e.g. columntype, etc.), so the following solution isn't really generic:

(defn- constraints [cname type min max]
  {(keyword cname)
   (as-> {:columntype (str type)} cmap
         (if min
           (assoc cmap :minInclude min)
           cmap)
         (if max
           (assoc cmap :maxInclude max)
           cmap))})

This basically builds up the result for each "row" in the resulting map. The one caveat here is the mapping between the Clojure types int and float to strings. I guess you don't really want a constraint on Clojure types. If I'm right, you should probably change the data in columntype to use keywords (i.e. :int, :float) and change from str to name above.

Then you can use mapv to map over all vectors, fetching the first, then the second value and so on from each vector simulatenously and feeding it to the constraints function:

(mapv constraints column maxInclude minInclude columntype) 

Here's a sample run:

user> (pprint (into {} (mapv constraints column columntype maxInclude minInclude)))
{:col1 {:columntype "clojure.core$int@607af697"},
 :col2 {:maxInclude 200, :columntype "clojure.core$float@4e8b32fb"},
 :col3
 {:maxInclude 300,
  :minInclude 1000,
  :columntype "clojure.core$int@607af697"},
 :col4 {:columntype "clojure.core$int@607af697"}}
nil