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))
Related
I began to learn Clojure two days ago, without any experience of functional programming. Today, when reading the reading through the book Programming Clojure, I met with a problem.
It's about the transforming sequence. There is an example:
(map #(format "<%s>%s</%s>" %1 %2 %1)
["h1" "h2" "h3" "h1"] ["the" "quick" "brown" "fox"])
which yields the result:
-> ("<h1>the</h1>" "<h2>quick</h2>" "<h3>brown</h3>" "<h1>fox</h1>")
It's not that hard for me to get it. Actually, the problem occurs when the book tells me we could use for to yield a sequence comprehension generally and then shows me an example. That example is kinda easy and I could totally understand it.
When I try to rewrite the example I first mentioned with for, the problem hit me.
I could just get:
("<h1>the</h1>"
"<h1>quick</h1>"
"<h1>brown</h1>"
"<h1>fox</h1>"
"<h2>the</h2>"
"<h2>quick</h2>"
"<h2>brown</h2>"
"<h2>fox</h2>"
"<h3>the</h3>"
"<h3>quick</h3>"
"<h3>brown</h3>"
"<h3>fox</h3>"
"<h1>the</h1>"
"<h1>quick</h1>"
"<h1>brown</h1>"
"<h1>fox</h1>")
with the rewrited code:
(for [label ["h1" "h2" "h3" "h1"] word ["the" "quick" "brown" "fox"]]
(format "<%s>%s</%s>" label word label))
I was informed that generally using :when clause could somehow help, but I just could not think it out.
How could I rewrite the code with for so that the answer is exactly the same as the map version?
As you've seen when you have multiple bindings in a for it acts like a "nested for loop" in other imperative languages, as if you had an outer for loop for label and an inner for loop for word. So you get every combination of the two collections' values.
for (label in labels)
for (word in words)
print(word + " " + label);
The simplest way I could imagine solving this problem with a for happens to also require map anyway, so I'd use your original simple map solution.
(def pairs ;; a vector of tuples/pairs of labels/words
(map vector ["h1" "h2" "h3" "h1"] ["the" "quick" "brown" "fox"]))
;; (["h1" "the"] ["h2" "quick"] ["h3" "brown"] ["h1" "fox"])
(for [[label word] pairs] ;; enumerate each pair
(format "<%s>%s</%s>" label word label))
=> ("<h1>the</h1>" "<h2>quick</h2>" "<h3>brown</h3>" "<h1>fox</h1>")
When you pass multiple collection args to map your mapping function receives an item from each collection for each mapping step. If you only had one input collection then the equivalent for would look very similar.
for produces a Cartesian product over all the given sequences, so one way to get corresponding pairs is to use map-indexed:
(for [[i label] (map-indexed vector ["h1" "h2" "h3" "h1"])
[j word] (map-indexed vector ["the" "quick" "brown" "fox"])
:when (= i j)]
(format "<%s>%s<%s>" label word label))
But this requires iterating over 16 values to produce 4 values, so using map with 3 arguments is both more efficient and simpler.
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.
How can I find the name "olle" by specifying :id and value of :id in
(def persons #{{:id 1 :name "olle"}
{:id 2 :name "anna"}})
I need to some function that would get the rows that match to be able to access the values and the keys.
I'm trying to implement SQL syntax in clojure, this is for a school assignment, so I don't need code, more like hints on how I should go about it. So far I know that the functions get-in and clojure.set/select could be used.
Ultimately, what I'm trying to achieve is this statement to be parsed and interpreted
select [name]
from persons
where id=1
->"olle"
This is what I've been testing with so far with no result
(persons :id 1 :name)
;=> :name "olle"
=> (clojure.set/select :id #{1})
#{}
=> (clojure.set/select odd? #{1})
#{1}
I've also played around with get-in, but still I have not managed to get the feel of moving around in the set and hash-maps within the set, so much that I can carry on with the coding, also I'm not sure if I need to define any grammar looking code in this assignment or if I can just do with writing algorithms?
First off, each definition is not a list, they are each a set where each element is a hash-map. Since this is an assignment, I won't provide the complete code, but I will say that your answer could make good use of group-by, get-in, and update-in.
Hint:
(clojure.set/select #(= 1 (:id %)) persons)
;=> #{{:name "olle", :id 1}}
(clojure.set/project *1 [:name])
;=> #{{:name "olle"}}
Presumably your assignment is asking you to come up with a macro transformation from a SQL-like DSL. So, you have two parts to figure out - First, how do I do this with normal functions? Second, how do I effect the transformation with macros?
Iam new to clojure and need some help to get a value out of a lazy sequence.
You can have a look at my full data structure here: http://pastebin.com/ynLJaLaP
What I need is the content of the title:
{: _content AlbumTitel2}
I managed to get a list of all _content values:
(def albumtitle (map #(str (get % :title)) photosets))
(println albumtitle)
and the result is:
({:_content AlbumTitel2} {:_content test} {:_content AlbumTitel} {:_content album123} {:_content speciale} {:_content neues B5 Album} {:_content Album Nr 2})
But how can I get the value of every :_content?
Any help would be appreciated!
Thanks!
You could simply do this
(map (comp :_content :title) photosets)
Keywords work as functions, so the composition with comp will first retrieve the :title value of each photoset and then further retrieve the :_content value of that value.
Alternatively this could be written as
(map #(get-in % [:title :_content]) photosets)
A semi alternative solution is to do
(->> data
(map :title)
(map :_content))
This take advances of the fact that keywords are functions and the so called thread last macro. What it does is injecting the result of the first expression in as the last argument of the second etc..
The above code gets converted to
(map :_content (map :title data))
Clearly not as readable, and not easy to expand later either.
PS I asume something went wrong when the data was pasted to the web, because:
{: _content AlbumTitel2}
Is not Clojure syntax, this however is:
{:_content "AlbumTitel2"}
No the whitespace after :, and "" around text. Just in case you might want to paste some Clojure some other time.
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.