I'm trying to learn clojure.
I am calling a function which is returning me an array of strings..
If I do:
(let [items (get-all-items)]
(println (type items))
(items))
the type of items is shown as class clojure.lang.PersistentVector where as the items value is like so:
[["Dogs"] ["Cats"] ["Capybaras"] ["Pygmy Hedgehogs"]]
I would like to convert this to a map in a format like this:
{ "Dogs" "Cats" "Capybaras" "Pygmy Hedgehogs" }
Does that make sense? Clojure maps can contain list of strings right?
I am only doing this because if I have it as a map, I can check if I have a pet in the list like this:
(contains? pets "Dogs")
; assuming the map is stored in pets variable
that fails if pets is a vector.
So If I can convert that to maps, how do I convert it? if not, how do I search for something in the vector?
(I like working with maps so I'd rather have maps - unless there is a strong reason not to do so)
ps: I've tried converting with into but that doesn't work either.
I suspect what you really want is a set, not a map. Maps store a value associated with a specific key. Sets store a unique list of values.
If your only use case is testing for membership, then you definitely want a set. You want a map if you are also associating some data with that key.
It's very easy to produce a set from any sequence. In your case:
(set (flatten items))
;;=> #{"Pygmy Hedgehogs" "Dogs" "Cats" "Capybaras"}
flatten removes the nesting on your lists giving you a sequence of strings. set consumes the sequence and returns a set of the unique values in that sequence.
(apply assoc {} (flatten [["Dogs"] ["Cats"] ["Capybaras"] ["Pygmy Hedgehogs"]]))
;;=> {"Capybaras" "Pygmy Hedgehogs", "Dogs" "Cats"}
Related
I am trying to retrieve data from a DB. The data is coming back in a lazy sequence. I can peek at the data in repl and it looks like this:
({:foo value1, :bar value2})
How can I get at this data? preferably, how can I turn it into a map? I've tried:
(doall sequence (get sequence :foo))
Which just returns nil. And
(apply hash-map user-settings)
which returns
llegalArgumentException No value supplied for key: {:foo value1, :bar value2} clojure.lang.PersistentHashMap.create (PersistentHashMap.java:77)
I am very new to clojure and have been stuck on this for way too long. Thanks in advance.
You already have a map, it just happens to be the only item in your list.
(def data (first '({:foo 123 :bar 456})))
(:foo data) ; => 123
Sometimes when you want to print lazy seq to see your data use into. For example if you want to see contents of a lazy vector use (into [] your-lazy-vector) or (into {} your-lazy-map).
You can do this uncool conversion within a println function or in a let. However, I recommend removing this kind of debugging aid before release or pull-request.
Lazy sequences are great, most of the time.
I declared a map in clojure using
(def finalMap {})
I am appending values to it inside a function using assoc but they are not appending, the map is remaining empty. I think it is due to immutability, can I in some way make a global map mutable.The function is a recursive one and I am appending values each time the function is called.
(defn func [arg1 arg2]
;(map append inside let)
(dorun (for [i (range 0 index)]
(do
(func(arg1 arg2))))))
Can you help me with the correct way to do this?
If you want a mutable map then you should create an atom:
(def final-map (atom {}))
Also normally you would use assoc to add more key value pairs to it. However you will need to use swap! just to be able to call assoc:
(swap! final-map assoc :a "a value")
This will add a key/value pair where the key is the keyword :a and the value is the String "a value".
It might be good to view some other examples of using assoc. Realise that in the code above assoc is being called with the old value of final-map as its first argument, and returning the new value of final-map.
Not sure how to phrase the question, but, I'm just playing around with the twitter api and clojure as a part of my wanting to learn clojure.
I am not sure what the clojure way of approaching this problem
I am trying to get first 5 tweets of all my followers. I can get the list of followers with the api, and I have a list of follower screen_name. Now, I have a function to get latest 5 tweets from a user. In C#, I would just declare a List<object> and add tweets to it inside a for loop. Clojure doesn't quite work that way.. so here's what I'm trying to do:
(defn get-tweets
[follower]
{:text (str "I am " follower)
:favs 0})
(defn get-all-followers-tweets
[]
(let [followers ["a" "b" "c"]
followers-tweets (map #(get-tweets %) followers)]
followers-tweets))
These are just mockups, but, you get the idea. Now, twitter returns something like this: [{:text "ssd" :fav 1} {:text "fed" :fav 2}]
so when I call get-all-followers-tweets, I get this:
(({:text "I am a", :favs 0}
{:text "I am b", :favs 0}
{:text "I am c", :favs 0}))
I don't know why the data is in 2 brackets, and I'm guessing it has something to do with map but, I just need the :text property from all collections.
doing (get response :text) or (get-in response [:text]) returns nil (assume response is the collection)
So, How do I get all the :text from the collection? Am I approaching this right? I tried (doseq [f followers] (get-tweets f)) and for but they seem very unnatural for getting just all the tweets.
What's the ideal clojure way of doing this?
Your get-tweets fn is returning a series of multiple maps, as a vector. You are then mapping that function over your followers, producing a sequence of sequences of maps. That's why there are two brackets - the outer sequence corresponds to the list of followers and each inner sequence is all the tweets from one follower grouped together.
I think the simplest approach if you're fine with discarding the identity of the authors is to use flatten, a function for unravelling nested sequential data structures to get just the items. That will give you just a sequence of maps without any grouping. You can then map :text over them to get just the texts.
e.g.
(defn get-all-followers-tweets
[]
(let [followers ["a" "b" "c"]
followers-tweets (map get-tweets followers)]
(flatten followers-tweets)))
(map :text (get-all-followers-tweets))
Maybe a more general solution is to consider mapcat, which stands for map-then-concat. It's the go-to approach when you have
a series of data items with some sort of internal structure.
that you want to "unpack" so that each produces one or more of the items you actually want.
It does this by mapping the given function over the outer items to produce a bunch of sequences and then concatenates all those sequences into one. But in this case our "unpacking function" is itself map so I don't think this approach is necessarily clearer here. That just makes it a little difficult to keep the different levels in mind:
(mapcat (partial map :text) (get-all-followers-tweets))
Being able to access key of (def my-hashmap {:one 1}) with (:one my-hashmap) is very convenient. Sometimes I have hashmaps that have string values.
I want to be able to be able to access (def my-hashmap {"one" 1}) with syntax like ("one" my-hashmap). Obviously, I can't. My options are either use (get my-hashmap "one") or to transform the hashmap so it has symbols for keys. I'd rather not, as I'll be passing the object to other functions which might expect it to be in the original format.
What is the shortest way I can look up a string key? Is get the only way or is there some magic?
The map can be used as a function
(my-hashmap "one")
If you're already passing the map as a function parameter or let-bound variable, you can also do destructuring of a map with string keys using the :strs keyword:
(let [{:strs [a]} {"a" 1, "b" 2}]
a)
; => 1
I have a function that queries my database for the X most recent entries, and it returns a vector of maps along the lines of:
[{:itemID "item1"
:category "stuff"
:price 5}
{:itemID "item2"
:category "stuff"
:price 54}
{:itemID "item3"
:category "stuff"
:price 435}
{:itemID "item4"
:category "otherstuff"
:price 32}]
How I go about destructuring a vector of maps(or is there a better method?) so that I can bind each value into a symbol along lines of:
item-1-id
item-1-category
item-1-cost
item-2-id
item-2-category
item-2-price
...etc
Having trouble grokking this, I get how to destructure a vector, or maps individually, but not a vector of maps, appreciate any help or insight.
That's simply impossible, since destructuring creates local bindings whose names must be known statically.
That's unless the total number of maps is known ahead of time, in which case you could of course write
(let [[{item-1-id :itemID ...} {item-2-id :itemID} ...] ...] ...)
The pattern could be captured in a macro, but the result would likely not be very pretty. (For example, introducing implicit bindings is not very pretty.)
A better solution might be to collect the various values in separate vectors:
(let [vector-of-maps (get-the-vector-of-maps)
ids (mapv :itemID vector-of-maps) ;; note the mapv
categories (mapv :category vector-of-maps)
...]
...)
Then you can say (ids 0) to refer to the ID from the first map, (categories 2) to refer to the category from the third map etc.
This works because vectors in Clojure act as functions of indices, returning the associated values (for example, ([:foo :bar] 0) returns :foo).
Or you could simply use vector-of-maps directly with get-in:
;; get ID from first map
(get-in vector-of-maps [0 :itemID])
See also assoc-in and update-in for producing modified versions of nested data structures.