I'm trying to sort a map by values (case insensitive). But it seems clojure sort function is case sensitive.
See below example ,
cljs.user=> (sort {:1 "A" :2 "Z" :3 "i"})
([:1 "A"] [:2 "Z"] [:3 "i"])
cljs.user=> (reverse (sort {:1 "A" :2 "Z" :3 "i"}))
([:3 "i"] [:2 "Z"] [:1 "A"])
Can anyone please suggest me a solutions for this how can I sort in clojure by case-insensitive ?
Java provides a case insensitive string comparator:
(def m {:1 "A" :2 "Z" :3 "i"})
(sort-by val String/CASE_INSENSITIVE_ORDER m) ;; => ([:1 "A"] [:3 "i"] [:2 "Z"])
this one would probably work:
user> (sort-by (comp clojure.string/lower-case val) {:1 "A" :2 "Z" :3 "i"})
;;=> ([:1 "A"] [:3 "i"] [:2 "Z"])
or for :rank from op's answer:
user> (sort-by (comp clojure.string/lower-case :rank) [{:rank "A"} {:rank "Z"} {:rank "i"}])
;;=> ({:rank "A"} {:rank "i"} {:rank "Z"})
The answer for this problem is
cljs.user=> (sort-by :rank #(> (clojure.string/upper-case %1) (clojure.string/upper-case %2)) [{:rank "A"} {:rank "Z"} {:rank "i"}])
({:rank "Z"} {:rank "i"} {:rank "A"})
Here we are comparing the values one by one after converting them to upper-case.
Related
I ve been playing around w/ Clojure for a while and I got stuck in something I think very trivial for many...but not me. I've the following piece of code;
;; Define a Record structure
(defrecord Person [first-name last-name age occupation])
(def john (->Person "John" "Frusciante" 50 "Guitarist"))
;; People map
(def people {"1" john
"2" (->Person "Pablo" "Neruda" 90 "Poet")
"3" (->Person "Stefan" "Zweig" 120 "Author")
}
)
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age %) 120) peeps)
)
(println "who's 120?: " (get-120-year-old-guy people))
This call returns an empty list. I know it's something wrong the way I retrieve the value but can't see what is that exactly.
You can get a hint of what's going on by changing the function temporarily:
(defn get-120-year-old-guy
[peeps]
(filter (fn [m] (println (type m) m)) peeps))
Prints:
(clojure.lang.MapEntry [1 #user.Person{:first-name John, :last-name Frusciante, :age 50, :occupation Guitarist}]
clojure.lang.MapEntry [2 #user.Person{:first-name Pablo, :last-name Neruda, :age 90, :occupation Poet}]
clojure.lang.MapEntry [3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}]
)
Note how each entry is a MapEntry. In your attempt, you're applying :age to the whole MapEntry (which returns nil), not just the person.
I think destructuring using a full anonymous function would be the easiest way:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ person]] (= (:age person) 120)) peeps))
Outputs:
who's 120?: ([3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
#leetwinski points out a more idiomatic solution that does away with the explicit function altogether:
(filter (comp #{120} :age val) people)
Broken down:
(defn get-120-year-old-guy [peeps]
(filter (comp ; And (comp)ose all three checks together
#{120} ; Then test if it's in the set of #{120}
:age ; Then get the age
val) ; Get the value from the MapEntry
peeps))
If you look at the first item in your outer map, you'll see that each item is a clojure.lang.MapEntry from string to Person. The key is "1" and the value is a Person record:
> (first people)
["1"
{:first-name "John",
:last-name "Frusciante",
:age 50,
:occupation "Guitarist"}]
To filter on the :age field, you have to first get the value of the {key, value} pair. One way is to use the val function to get that before getting :age from the Person map. Your filter function is then:
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age (val %)) 120) peeps)
)
> (println "who's 120?: " (get-120-year-old-guy people))
who's 120?: ([3 #challenges.anag.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
Another option is to destructure the clojure.lang.MapEntry:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ v]] (= (:age v) 120)) peeps)
)
Here you can see (fn [[_ v]] ... where the _ is used as a place holder for the unused key.
I have a single vector of vectors and I want to separate them into individual vectors.
[["1" "2" "3"] ["a" "b" "c"]]
;;=> ["1" "2" "3"] ["a" "b" "c"]
[["1" "2" "3"] ["a" "b" "c"] ["10" "20" "30"]]
;=> ["1" "2" "3"] ["a" "b" "c"] ["10" "20" "30"]
You already have individual vectors inside of your vector. A variety of ways exist to access them, most notably nth.
Separation can happen in many ways. Here are some examples that you can try out at a REPL.
A common pattern is to use let with to bind them individually in a local context:
(let [first-elem (nth my-vec 0)
third-elem (nth my-vec 2)]
(str "First: " first-elem "\Third: " third-elem))
This is often done via destructoring, too:
(let [[first-elem _ third-elem] my-vec] ;; _ is idiomatic for ignored binding
(str "First: " first-elem "\Third: " third-elem))
Another common scenario is building a lazy sequence from the individual elements, like:
(map-indexed (fn [i v] (str "Elem " i ": " v)) my-vec)
Or just iteration for side-effects
(doseq [v my-vec]
(println v))
;; in Clojure 1.7 prefer
(run! println my-vec)
Or storing one as mutable state
(def current-choice (atom nil))
(swap! current-choice (nth my-vec 2))
#current-choice
You will discover many more as you learn about Clojure collections. Continue experimentation at will.
Here is the situation: I have a vector of vectors ("data"), a set of headers, a subset of headers ("primary headers"), a constant ("C"), an element-wise function ("f"), and the remaining headers ("secondary headers"). My goal is to take the "data" and produce a new vector of vectors.
Example data:
[[1.0 "A" 2.0]
[1.0 "B" 4.0]]
Example headers:
["o1" "i1" "i2"]
Example primary headers:
["i1" "i2"]
Example secondary headers:
["o1"]
Example new vector of vectors:
[[(f "A") (f 2.0) C (f 1.0)]
[(f "B") (f 4.0) C (f 1.0)]]
My current attempt is to mapv each row, then map-indexed each element with an if to check for primary membership, then the constant, then map-indexed each element with an if to check for secondary membership, finally conj on the results. But I am not getting it to work right.
Example code:
(mapv (fn [row] (conj (vec (flatten (map-indexed
(fn [idx item] (let [header-name (nth headers idx)]
(if (= (some #{header-name} primary-headers) headers-name) (f item))))
row)))
C
(vec (flatten (map-indexed
(fn [idx item] (let [header-name (nth headers idx)]
(if (= (some #{header-name} secondary-headers) headers-name) (f item))))
row)))))
data)
You should consider using core.matrix for stuff like this. It is a very flexible tool for multi-dimensional array programming in Clojure.
Most array-manipulation operations are likely to be 1-2 liners.....
(def DATA [[1.0 "A" 2.0]
[1.0 "B" 4.0]])
(emap (partial str "f:") (transpose (mapv #(get-column DATA %) [1 0 2])))
=> [["f:A" "f:1.0" "f:2.0"]
["f:B" "f:1.0" "f:4.0"]]
You might need to look up the column names to calculate the [1 0 2] vector but hopefully this gives you a good idea how to do this....
Not sure if I got your problem right, but looks like you want something like this:
(defn magic [data h p s f]
(let [idx (map (into {} (map-indexed #(vector %2 %1) h))
(concat p s))]
(mapv #(mapv (comp f (partial get %))
idx)
data)))
Here is an example of my magic function:
(magic [[1.0 "A" 2.0]
[1.0 "B" 4.0]]
["o1" "i1" "i2"]
["i1" "i2"]
["o1"]
#(str "<" % ">"))
[["<A>" "<2.0>" "<1.0>"]
["<B>" "<4.0>" "<1.0>"]]
Let's get a closer look at it.
First of all, I'm calculating permutation index idx. In your case it's (1 2 0). In order to calculate it I'm turning ["o1" "i1" "i2"] into a hash map {"o1" 0, "i1" 1, "i2" 2} and then using it on ("i1" "i2" "o1") sequence of primary and secondary headers.
Then I'm using idx to rearrange data matrix. On this step I'm also applying f function to each element of new rearranged matrix.
Update
I thought that it'll be best to split my complicated magic function into three simpler ones:
(defn getPermutation [h1 h2]
(map (into {} (map-indexed #(vector %2 %1) h1))
h2))
(defn permutate [idx data]
(mapv #(mapv (partial get %) idx)
data)))
(defn mmap [f data]
(mapv (partial mapv f)
data))
Each function here is atomic (i.e. performing a single task), and they all could be easily combined to do exactly what magic function do:
(defn magic [data h p s f]
(let [idx (getPermutation h (concat p s))]
(->> data
(permutate idx)
(mmap f))))
getPermutation function here calculates idx permutation index vector.
permutate rearranges columns of a matrix data according to given idx vector.
mmap applies function f to each element of a matrix data.
Update 2
Last time I missed the part about adding a constant. So, in order to do so we'll need to change some of the code. Let's change permutate function allowing it to insert new values to the matrix.
(defn permutate [idx data & [default-val]]
(mapv #(mapv (partial get %) idx (repeat default-val))
data)))
Now, it'll use default-val if it won't be able to get the element with the specified index idx.
We'll also need a new magic function:
(defn magic2 [data h p s f c]
(let [idx (getPermutation h (concat p [nil] s))]
(permutate idx (mmap f data) c)))
I changed the order of applying mmap and permutate functions because it seems that you don't want to apply f to your constant.
And it works:
(magic2 [[1.0 "A" 2.0]
[1.0 "B" 4.0]]
["o1" "i1" "i2"]
["i1" "i2"]
["o1"]
#(str "<" % ">")
"-->")
[["<A>" "<2.0>" "-->" "<1.0>"]
["<B>" "<4.0>" "-->" "<1.0>"]]
Given
(def data [[1.0 "A" 2.0] [1.0 "B" 4.0]])
(def headers ["o1" "i1" "i2"])
(def primaries ["i1" "i2"])
(def secondaries ["o1"])
(defn invert-sequence [s] (into {} (map-indexed (fn [i x] [x i]) s)))
... this does the job:
(defn produce [hs ps ss f data const]
(let [perms (map #(mapv (invert-sequence hs) %) [ps ss])]
(mapv (fn [v] (->> perms
(map #(map (comp f v) %))
(interpose [const])
(apply concat)
vec))
data)))
Using the example in the question:
(produce headers primaries secondaries #(list 'f %) data 'C)
; [[(f "A") (f 2.0) C (f 1.0)] [(f "B") (f 4.0) C (f 1.0)]]
Using Leonid Beschastny's example:
(produce headers primaries secondaries #(str "<" % ">") data 'C)
; [["<A>" "<2.0>" C "<1.0>"] ["<B>" "<4.0>" C "<1.0>"]]
Using str:
(produce headers primaries secondaries str data 'C)
; [["A" "2.0" C "1.0"] ["B" "4.0" C "1.0"]]
Using identity:
(produce headers primaries secondaries identity data 'C)
; [["A" 2.0 C 1.0] ["B" 4.0 C 1.0]]
Here's my problem: I want a function helpme that takes a map and replaces the keys :r and :g with empty vectors if and only if those keys exist. For example:
Input:
(helpme {:a "1" :r ["1" "2" "3"] :g ["4" "5"]})
Output:
{:a "1" :r [] :g []}
Input:
(helpme {:a "1" :r ["1" "2" "3"]})
Output:
{:a "1" :r []}
I can define a function "helpme" that does this, but it's overly complicated, and I feel like there must be an easier (more idiomatic) way...
Here's the overly complicated way I've done, as requested below:
(defn c [new-doc k] (if (contains? new-doc k) (assoc new-doc k []) new-doc))
(defn helpme [new-doc] (c (c new-doc :r) :g))
(defn helpme [m]
(into m (for [[k _] (select-keys m [:r :g])]
[k []])))
Short, and only requires editing in one place when the number of items to set to [] changes.
In my search for a version of update-in which only updated the map if the key actually existed, Google insisted that I could find my answer here. For others in search of the same thing I've created the following helper functions:
(defn contains-in?
[m ks]
(not= ::absent (get-in m ks ::absent)))
(defn update-if-contains
[m ks f & args]
(if (contains-in? m ks)
(apply (partial update-in m ks f) args)
m))
That way you could:
> (def my-data {:a {:aa "aaa"}})
> (update-if-contains my-data [:a :aa] clojure.string/upper-case)
{:a {:aa "AAA"}}
> (update-if-contains my-data [:a :aa] clojure.string/split #" ")
{:a {:aa ["a" "aa"]}}
> (update-if-contains my-data [:a :b] clojure.string/upper-case)
{:a {:aa "aaa"}} ; no change because [:a :b] didn't exist in the map
(defn helpme
[mp]
(as-> mp m
(or (and (contains? m :r) (assoc m :r []))
m)
(or (and (contains? m :g) (assoc m :g []))
m)
m))
if there were a third replacement, I would use this function:
(defn replace-contained [m k v] (or (and (contains? m k) (assoc m k v)) m))
as-> is new in clojure 1.5 but the definition is very simple if you are stuck using an older clojure version:
(defmacro as->
"Binds name to expr, evaluates the first form in the lexical context
of that binding, then binds name to that result, repeating for each
successive form, returning the result of the last form."
{:added "1.5"}
[expr name & forms]
`(let [~name ~expr
~#(interleave (repeat name) forms)]
~name))
what about using cond-> for that purpose?
(defn helpme [m]
(cond-> m
(:r m) (assoc :r [])
(:g m) (assoc :g [])))
One option:
(defn helpme [m]
(merge m
(apply hash-map (interleave
(clojure.set/intersection
(set (keys m)) #{:r :g})
(repeat [])))))
If this is really as simple as conditionally setting the value of two fixed keys, I'd just write it out long hand to keep it simple.
(defn clean [m]
(let [m (if (:r m) (assoc m :r []) m)
m (if (:g m) (assoc m :g []) m)]
m))
If you want something more general and reusable, here's an option:
(defn cond-assoc [m & kvs]
(reduce
(fn [acc [k v]]
(if (get acc k)
(assoc acc k v)
acc))
m
(partition 2 kvs)))
(cond-assoc {:a "1" :r ["1" "2" "3"] :g ["4" "5"]}
:r []
:g []) ; {:r [] :a "1" :g []}
(cond-assoc {:a "1" :r ["1" "2" "3"]}
:r []
:g []) ; {:r [] :a "1"}
By testing for the expected key in the compare function
(sort-by #(if (number? (:priority %))
(:priority %)
(java.lang.Integer/MAX_VALUE))
<
[{:priority 100} {:priority 10} {:test 1}])
>({:priority 10} {:priority 100} {:test 1})
I really like the helpme API to include the keys to reset and the default value to reset to:
(defn helpme [m & {:keys [ks v]
:or {ks #{:r :g}
v []}}]
(apply assoc m (-> m
;; select only existing keys by selecting from original map
(select-keys ks)
keys
;; generate defaults for each (handled by applying `assoc`)
(interleave (repeat v)))))
This uses assocs variadic form by production the arguments to it.
If you give up the general API it can be as short as:
(defn helpme [m]
(apply assoc m (-> m
(select-keys #{:r :g})
keys
(interleave (repeat [])))))
Input:
List of keys: [ :name :address :work]
Map 1: { :name "A" :address "A Street" }
Map 2: { :work "Work Ave" }
Output:
([:name "A" nil] [:address "A Street" nil] [:work nil "Work Ave"])
This is what I have at the moment:
(defn maps-iterate [v & ms]
(map (fn [k] (into [] [k #(map (k %) ms)])) v))
(println (maps-iterate [ :name :address :work ] { :name "A" :address "A Street"} { :work "Work Ave" }))
Which gives me:
([:name #<user$maps_iterate$fn__2$fn__3 user$maps_iterate$fn__2$fn__3#4b14b82b>]
[:address #<user$maps_iterate$fn__2$fn__3 user$maps_iterate$fn__2$fn__3#3d47358f>]
[:work #<user$maps_iterate$fn__2$fn__3 user$maps_iterate$fn__2$fn__3#e0d5eb7>])
Try
(defn maps-iterate [v & ms]
(map (fn [k] (into [] [k (map #(k %) ms)])) v))
Or even better:
(defn maps-iterate [v & ms]
(map (fn [k] (cons k (map k ms))) v))
Note: if all keys are keywords, then you can use them as functions: (map k ms) instead of (map #(k %) ms). If they are not then you can't use them as functions. You need to write (map #(% k) ms)
How about this?
(for [k ks]
[k (map k [m1 m2])])
;;=> ([:name ("A" nil)] [:address ("A Street" nil)] [:work (nil "Work Ave")])
or, if you really want a flat vector in the results:
(for [k ks]
(apply vector k
(map k [m1 m2])))
;;=> ([:name "A" nil] [:address "A Street" nil] [:work nil "Work Ave"])
user=> (def a { :name "A" :address "A Street" })
#'user/a
user=> (def b { :work "Work Ave" })
#'user/b
user=> (def c [ :name :address :work])
#'user/c
user=> (map #(vector %1 (%1 a) (%1 b)) c)
([:name "A" nil] [:address "A Street" nil] [:work nil "Work Ave"])
The following REPL example produces your output without the nil values indicated:
user> (def map1 { :name "A" :address "A Street" })
#'user/map1
user> (def map2 { :work "Work Ave" })
#'user/map2
user> (seq (merge map1 map2))
([:work "Work Ave"] [:name "A"] [:address "A Street"])