Threaded comments in Clojure - clojure

I would like to be able to make inserts in a tree data structure (like the one doing comments on Disqus, Hacker News etc). And it would be nice to do it in a clever functional way.
Example
(def cmts [{:name "Abi" :id 1 :text "Great question" :children nil}
{:name "Bib" :id 2 :text "What about zippers?" :children
[{:name "Linus" :id 3
:text "I don't understand how to and insert
children at a certain id with them"
:children nil}]}])
The problem is how to insert a comment like this
(add-comment cmts :name "Iba" :text "I think so too!" :in-reply-to 1)
in a somehow concise/elegant way.
Or: what would be a simpler way to solve the problem?

If you are looking to do functional tree editing (editing neste data structures) then perhaps
the zipper library is the right tool.

I realize that there are very good functionality in the clojure.walk library that could do the trick. http://clojuredocs.org/clojure_core/clojure.walk

Related

Clojure form - Submit when press enter

I have a search-form that searches some text for me. When I type in the input box I have to manually press a button for it to search. Is there a way for me to hit enter on the keyboard and have that search, as well as the button?
(defn search-form
[]
[:div
[:p "What are you searching for? "
[:input
{:type :text
:name :search
:on-change #(swap! fields assoc :search (-> % .-target .-value))
:value (:search #fields)}]]
[:input
{:type :submit
:value :Search
:on-click #(do
(search-function (:search #fields)))}]
[#search-results]])
This is the code I currently have. As you can see, to call the search-function I have to click on the button. I would like to be able to press enter and also have the ability to press the button and both will call search-function
Any help would be much appreciated. Thanks
You might try something like :on-key-down for the event, and I think you are looking for the number 13. Ran across it when I was reading the source code for reagent-forms though I am not sure where the documentation would be.
Closest documentation that I could find is here, though React tests for the Enter key explicitly here.

Slow Datascript query

I'm using Datascript to query a tree structure for the last common ancestor of
2 nodes having given names, here's what I've got so far, but it's really slow
-- any idea why (or is there a better way)?
(defn lca
"Last common ancestor"
[db name1 name2]
(d/q '[
:find [(pull ?anc [:db/id :name]) ...]
:in $ % ?name1 ?name2
:where
(?node1 :name ?name1)
(?node2 :name ?name2)
(anc ?anc1 ?node1)
(anc ?anc2 ?node2)
[(not= ?anc1 ?anc2)]
(parent ?anc ?anc1)
(parent ?anc ?anc2)
]
#db
'[
[ (parent ?par ?child)
(?par :children ?child)]
[ (anc ?par ?child)
(?par :children ?child)]
[ (anc ?anc ?child)
(?par :children ?child)
(anc ?anc ?par)]
]
name1
name2))
I initially was going to use not to exclude all higher ancestors than the
last common one, but Datascript currently doesn't support not hence the two
parent clauses.
The schema is:
:children {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db/index true}
:name {:db/index true}
Well, recursive rules are not the fastest thing in DataScript. So you can probably make your query a little bit faster by inlining parent rule directly into query code.
Another thing is that queries are not the fastest thing is DataScript as well. There’s fair amount of time spent parsing the query, allocating intermediate collections, iterating over them, managing variables, etc. There’re two situations in which you can prefer query over manual database/indexes access:
Query works faster than you’d write yourself (e.g. when working with large relations, query will utilize hash joins, a thing that is very tedious to write manually)
Query express your problem in a much simpler way than imperative algorithm
In your case, neither of these is true (you don’t really work with relations, you walk the graph linearly). Also, there’s a bug: your query won’t work if node1 and node2 have common direct parent.
What I recommend is to do the same thing by accessing entities directly. Entities are just index lookups without any overhead associated with queries, so in such a simple case they should work much faster.
Something like this should suffice:
(defn parent [node]
(first (:_children node)))
(defn ancestors [node]
(->> node
(iterate parent)
(take-while some?)
reverse))
(defn last-common-ancestor [db name1 name2]
(let [node1 (d/entity db [:name name1])
node2 (d/entity db [:name name2])]
;; zipping ancestor chains together
(->> (map vector (ancestors node1) (ancestors node2))
;; selecting common prefix
(take-while (fn [[ac1 ac2]] (= ac1 ac2)))
;; last item in common prefix is what you looking for
(last))))

Clojure transform map values which are vectors to set

I am new at Clojure. I have a map like
{:title "The Little Schemer"
:authors [friedman , felleisen]}
I want to transform it to:
{:title "The Little Schemer"
:authors #{friedman , felleisen}}
I attempted like:
(def friedman {:name "Daniel Friedman" :birth-year 1944})
(def felleisen {:name "Matthias Felleisen"})
(defn old-book->new-book [book]
(set (:authors book)
)
)
(println (old-book->new-book {:title "The Little Schemer"
:authors [friedman , felleisen]}))
; => Output: #{{:name Daniel Friedman, :birth-year 1944} {:name Matthias Felleisen}}
; => Expected-Output: #{friedman , felleisen}
Here the defs friedman and felleisen gets executed and there results are getting transformed to set. But, I want the function names to be converted to set instead of their results.
First of all try to println this:
(println {:title "The Little Schemer"
:authors [friedman , felleisen]})
The output will be:
{:title The Little Schemer, :authors [{:name Daniel Friedman, :birth-year 1944} {:name Matthias Felleisen}]}
So, what happened here? As You know in this context friedman and felleisen is a variables, so, if You print them - they will be appear in a print message by values. For example:
(def a 1)
(println a)
Will print 1, because of a is just a variable.
The code which you shown here do what You want and when you print it - then values of variable of friedman and felleisen substitute by values.
Your vector of autors((:authors [friedman felleisen])) after disposing the function set will be converted to a set, what we saw from Your output.

Array-map example in clojure

I am learning clojure and trying to implement a problem. I am storing maps in a vector. Each map contains an id. For example [{:id 1 :name "abc"} {:id 2 :name "xyz"}]. The map also contains some more fields.
I read somewhere that, instead of using a vector to store the maps, I could use an array-map and do away with my id and store it something like {1 {:name "abc"}, 2 {:name "xyz"}}.
I tried going through the clojure docs but didn't find a good example to achieve this. Can some please help me out and give me a good example?
You can use assoc to add values to a map. assoc takes 3 args. The first arg is the map that you want to add to, 2nd arg is a key, and the third is a value. The function returns the old map with the key-value pair added.
Example:
(assoc {} 1 {:name "abc"})
returns
{1 {:name "abc"}}
Your idea is to lift the :id entry of each record-map into an index, while removing it from the map. You end up with a map of :id-less records instead of a vector of full records.
The following function lifts the key fk out of the collection of maps ms:
(defn key-by [fk ms]
(into {} (map (fn [m] [(get m fk) (dissoc m fk)]) ms)))
For example,
(key-by :id [{:id 1 :name "abc"} {:id 2 :name "xyz"}])
;{1 {:name "abc"}, 2 {:name "xyz"}}
Note:
Every record should have an :id.
Your :ids had better be distinct, or you'll lose records.
Don't depend on array-map: it's an implementation detail. A
modified version might well be a hash-map.
If you need your map sorted by key, use a sorted-map.
If you need to keep your records in insertion order, think again.

How to get specific data from a set that holds hashmaps in clojure by specifying keyword and value

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?