Reputation: 1174
Basically what I'm trying to do is print a hashmap that contains keys that are characters in a string, and these keys have value 1. For example, string "aabbce" should give a dictionary {:a 1 :b 1 :c 1}. The following is my attempt but it just prints the empty hashmap
(defn isValid [s]
(def dict {})
(map (fn [x] ((assoc dict :x 1))) (seq s))
(println dict)
)
Upvotes: 0
Views: 127
Reputation: 13473
You've broken several guidelines of Clojure programming:
def
for locals. Use let
.To do the last thing here, use reduce
instead of map
. And the seq
is redundant: functions such as map
and reduce
will treat a string as a sequence of characters. So ...
(defn isValid [s]
(reduce (fn [dict x] (assoc dict x 1)) {} s))
For example ...
=> (isValid "aabbce" )
{\a 1, \b 1, \c 1, \e 1}
The local dict
and the initial value {}
have been captured by the reduce
.
Why map to 1
? Is this just a set in disguise? If so, ...
(defn isValid [s]
(set s))
Or just ...
(def isValid set)
For example,
=> (isValid "aabbce" )
#{\a \b \c \e}
You'll find this with functional programming. Boilerplate code melts away like snow in a Chinook wind.
A final trivial gripe. isValid
is camel case. The Clojure conventions are
?
for predicates.So valid?
instead of isValid
.
Upvotes: 2
Reputation: 3346
There are lots of ways to implement this leveraging on the large number of functions in the Clojure core library. This is one of the common issues that happens to every new dev coming to Clojure: You think you need to write a function yourself, but actually something already exists, it's just that you don't know it's name yet, so the Clojure cheat-sheet might come handy.
Let's start with the string aabbce
. You want to remove the duplicates, so (set "aabbce")
would read the string as a collection of chars and create a set out of them. You can use the map
function to take each character and turn it into a keyword. The problem is that the keyword
function takes a string, not a char, so we need to use str
on each char first.
Once we have a sequence of keywords, one simple way of constructing the map is using the frequencies
function, it will create a hash-map of each element in the collection of keywords with the keyword as the key, and the number of times it appears in the collection as the value, and since we already removed the duplicates, we're guaranteed that each value will be just 1
.
Putting everything together:
(defn is-valid? [s]
(frequencies
(map (comp keyword str) ;; Turns \a into :a for every letter in the set
(set s)))) ;; remove duplicates, yields: (\a, \b, \c, \e)
;; or, using the ->> macro:
(defn is-valid? [s]
(->> (set s) ;; turn string into set of chars
(map (comp keyword str)) ;; turn chars into keywords
frequencies)) ;; compute occurrences hash-map
;; (println (is-valid? "aabbce"))
;; => {:a 1, :b 1, :c 1, :e 1}
Now, the name isValid suggests that you want to use this function as a predicate (eg. return true
or false
depending on the input). Maybe want to build a function that checks that a string is composed of certain letters?
Upvotes: 0
Reputation: 17859
another way is:
(zipmap (map (comp keyword str) "abc") (repeat 1))
;;=> { :a 1 :b 1 :c 1 }
Upvotes: 5
Reputation: 10865
> (into {} (for [c "aabbce"] [(keyword (str c)) 1]))
{:a 1, :b 1, :c 1, :e 1}
into {} ... sequence of pairs ...
is often a convenient way to create hashmaps. For example
> (into {} [[:x 1] [:y "foo"]])
{:x 1, :y "foo"}
and for [item collection] [(key-from item) (value-from item)]
can be a nice way to iterate over a collection to create that list of key-value pairs.
> (for [color ["red" "blue" "green"]] [(clojure.string/upper-case color) (count color)])
(["RED" 3] ["BLUE" 4] ["GREEN" 5])
I find that putting those together is often the trick for when I want to create a hashmap:
> (into {} (for [color ["red" "blue" "green"]] [(clojure.string/upper-case color) (count color)]))
{"RED" 3, "BLUE" 4, "GREEN" 5}
Upvotes: 1