Clojure: Update value of record field - clojure

I have defined record to store User details and Address Details.
(defrecord User [id name address])
(defrecord Address [id location street city state])
(def usr (User. 1 "Abc"
(Address. 1 "Location 1" "Street" "NY" "US")))
I have updated "name" to "BCD" using the below code
(assoc usr :name "BCD")
Output:
#async_tea_party.core.User{:id 1, :name "BCD", :address #async_tea_party.core.Address{:id 1, :location "Location 1", :street "Street", :city "NY", :state "US"}}
(usr)
OutPut:
#async_tea_party.core.User{:id 1, :name "Abc", :address #async_tea_party.core.Address{:id 1, :location "Location 1", :street "Street", :city "NY", :state "US"}}
New value of name field has not updated and It still shows old value.
How can I update "name" field permanently in "User" record?

(def usr (User...)) is kind of immutable. You cannot change it.
When you do (assoc usr :name "BCD") you are not changing it. You create a new one. In order to do what you want you need an atom.
(def usr (atom (User. 1 "Abc"
(Address. 1 "Location 1" "Street" "NY" "US"))))
(:name #usr) ;; "Abc"
(swap! usr assoc :name "BCD")
(:name #usr) ;; "BCD"

This is called immutability and is one of the main reasons for me to like clojure so much.
To understand the reasoning why this behaviour is so beneficial, reading values and state really helped me

Related

Clojure Map with # literal

What is the difference between
#:user{:profile{:name "Sally Clojurian"
:address {:city "Austin" :state "TX"}}}
and
{:user {:profile {:name "Sally Clojurian"
:address {:city "Austin" :state "TX"}}}}
I know that for the last on if I want to get name I can just do :
(get-in [:profile :name] map)
How would I get name for the first map?
The syntax of printing a map beginning with #:qualifer{ ... } is an abbreviated form of printing when all of the keys of the map are keywords with the same qualifier, or namespace. You can cause it to print without that abbreviation as shown below:
$ clojure
Clojure 1.10.1
user=> (def m1 #:user{:profile{:name "Sally Clojurian"
:address {:city "Austin" :state "TX"}}})
#'user/m1
user=> (pr m1)
#:user{:profile {:name "Sally Clojurian", :address {:city "Austin", :state "TX"}}}nil
user=> (doc *print-namespace-maps*)
-------------------------
clojure.core/*print-namespace-maps*
*print-namespace-maps* controls whether the printer will print
namespace map literal syntax. It defaults to false, but the REPL binds
to true.
nil
user=> (binding [*print-namespace-maps* false] (pr m1))
{:user/profile {:name "Sally Clojurian", :address {:city "Austin", :state "TX"}}}nil
If a map is def'ed as:
(def myMap #:user{:profile{:name "Sally Clojurian"
:address {:city "Austin" :state "TX"}}})
than to get to profile name you can do something like :
(get-in map [:user/profile :name])

In clojure how to refresh the table data

I have a table created using seesaw.swingx and i want to refresh the data in the rows of the table (or even clearing the entire table and giving new data to it will do). How do i achieve this, I know i might have to use table/table-model, but how do i give this table model to my current table?
my table is created as
(swingx/table-x :id :data-table
:horizontal-scroll-enabled? true
:model [:columns [{:key :first-name :text "First Name"}
{:key :last-name :text "Last Name"}]
:rows (get-data)]))
EDIT:
So this is my handler where i want to update my table
(defn- return-movie-handler
[event]
(let [root (seesaw/to-root event)
table (seesaw/select root [:#data-table])]
;some code
(seesaw/replace! root table (get-table-model))))))
and my get-table-model is
(defn- get-table-model
[]
(seesaw.table/table-model :columns [{:key :first-name :text "First Name"}
{:key :last-name :text "Last Name"}]
:rows (get-data)))
doing this i get an exception java.lang.IllegalArgumentException: No implementation of method: :make-widget* of protocol: #'seesaw.make-widget/MakeWidget found for class: seesaw.table.proxy$javax.swing.table.DefaultTableModel
you may use replace! , http://daveray.github.io/seesaw/seesaw.core-api.html#seesaw.core/replace!
(replace! Container Old widget Table Model)
Here is updated code I base on your code. I tested on my local ,works good.
(use 'seesaw.core)
(defn- get-table-model
[a b]
(seesaw.core/table :model [:columns [ :first-name :last-name]
:rows [[ a b]]]))
(def base_frame (frame :title "Base Frame" :content (vertical-panel :items [(border-panel :north (get-table-model "a" "b" ) :id :panel_id)])))
(show! base_frame)
(pack! base_frame)
(replace! (select base_frame [:#panel_id]) (first (select base_frame [:<javax.swing.JTable>])) (get-table-model "C" "D") )
It's a bit late, but you can also use (config! (select root [:#data-table]) :model new-model), having kept the model in an atom or using a generator function. Much simpler than (replace!) imo.

Add items from collection 1 to collection 2, if collection 2 doesn't contain item from collection 1

I've got two maps:
(def people {:1 "John" :2 "Paul" :3 "Ringo" :4 "George"})
(def band
{:data
{:members
{:1 {:id 1 :name "John"}
:2 {:id 2 :name "Paul"}}}})
I want to loop over people and add any members that don't exist in [:data :members] to band, resulting in:
(def band
{:data
{:members
{:1 {:id 1 :name "John"}
:2 {:id 2 :name "Paul"}
:3 {:id 3 :name "Ringo"}
:4 {:id 4 :name "George"}}}})
Here's what I've tried:
(for [[id name] people]
(when-not
(contains? (get-in band [:data :members]) id)
(assoc-in band [:data :members id] {:id id :name name})))
Which yields:
({:data
{:members
{:4 {:id :4, :name "George"},
:1 {:name "John", :id 1},
:2 {:name "Paul", :id 2}}}}
nil
nil
{:data
{:members
{:1 {:name "John", :id 1},
:2 {:name "Paul", :id 2},
:3 {:id :3, :name "Ringo"}}}})
I'm not sure why I'm getting back what looks to be a list of each mutation of band. What am I doing wrong here? How can I add the missing members of people to band [:data :members]?
To be pedantic you aren't getting back any mutation of band. In fact, one of the most important features of Clojure is that the standard types are immutible, and the primary collection operations return a modified copy without changing the original.
Also, for in Clojure is not a loop, it is a list comprehension. This is why it always returns a sequence of each step. So instead of altering an input one step at a time, you made a new variation on the input for each step, each derived from the immutable original.
The standard construct for making a series of updated copies of an input based on a sequence of values is reduce, which passes a new version of the accumulator and each element of the list to your function.
Finally, you are misunderstanding the role of :keyword syntax - prefixing an item with a : is not needed in order to construct map keys - just about any clojure value is a valid key for a map, and keywords are just a convenient idiom.
user=> (def band
{:data
{:members
{1 {:id 1 :name "John"}
2 {:id 2 :name "Paul"}}}})
#'user/band
user=> (def people {1 "John" 2 "Paul" 3 "Ringo" 4 "George"})
#'user/people
user=> (pprint
(reduce (fn [band [id name :as person]]
(if-not (contains? (get-in band [:data :members]) id)
(assoc-in band [:data :members id] {:id id :name name})
band))
band
people))
{:data
{:members
{3 {:id 3, :name "Ringo"},
4 {:id 4, :name "George"},
1 {:name "John", :id 1},
2 {:name "Paul", :id 2}}}}
nil
You may notice the body of the fn passed to reduce is essentially the same as the body of your for comprehension. The difference is that instead of when-not which returns nil on the alternate case, I use if-not, which allows us to propagate the accumulator (here called band, same as the input) regardless of whether any new version of it is made.

Display column names in table

I have created a table "sampletable". I am trying to display the name of the columns. I tried adding the text but it still doesn't show the names.
(def sampletable
(seesaw/table :model
[:columns [{:key :name, :text "Name"} :likes]
:rows [["Bobby" "Laura Palmer"]
["Agent Cooper" "Cherry Pie"]
{:likes "Laura Palmer" :name "James"}
{:name "Big Ed" :likes "Norma Jennings"}]]))
Any help would be appreciated.
Try this
(require '[seesaw.table])
(def sampletable
(seesaw/table
:model (seesaw.table/table-model
:columns [{:key :name, :text "Name"} :likes]
:rows [["Bobby" "Laura Palmer"]
["Agent Cooper" "Cherry Pie"]
{:likes "Laura Palmer" :name "James"}
{:name "Big Ed" :likes "Norma Jennings"}])))

Clojure elastic search API Elastisch not returning results or documents

I am trying out clojure elastic search API Elastisch.
I was following the demo code given in the documentation
I am getting document/index created output for the following code
(defn demo-search-connect []
(esr/connect! "http://127.0.0.1:9200")
(let [mapping-types {:person {:properties {:username {:type "string" :store "yes"}
:first-name {:type "string" :store "yes"}
:last-name {:type "string"}
:age {:type "integer"}
:title {:type "string" :analyzer
"snowball"}
:planet {:type "string"}
:biography {:type "string" :analyzer "snowball" :term_vector "with_positions_offsets"}}}}
doc {:username "happyjoe" :first-name "Joe" :last-name "Smith" :age 30 :title "Teh Boss" :planet "Earth" :biography "N/A"}]
(esi/create "myapp2_development" :mappings mapping-types)
;; adds a document to the index, id is automatically generated by ElasticSearch
;= {:ok true, :_index people, :_type person, :_id "2vr8sP-LTRWhSKOxyWOi_Q", :_version 1}
(println (esd/create "myapp2_development" :person doc :settings {"number_of_shards" 1}))
))
;Supposed to return an output for the search query
(defn demo-search-index []
(esr/connect! "http://127.0.0.1:9200")
(esd/search "myapp2_development" "person" :query {:term {:last_name "Smith"}})
)
;Supposed to return the document with the given id
(defn get-document [id]
(esr/connect! "http://127.0.0.1:9200")
(esd/get "myapp2_development" "person" id)
)
I am getting the output for the first function as :
{:ok true, :_index myapp2_development, :_type :person, :_id GzNdzrqhQECQDlkSbq-GHA, :_version 1}
From the output I believe the document is getting indexed properly
The issue is that the second and third functions returns:
{:took 153, :timed_out false, :_shards {:total 5, :successful 5, :failed 0}, :hits {:total 0, :max_score nil, :hits []}}
and nil respectively.
What am I missing here?
P.S : I am new to clojure and elastic search
esd/get fails because your mapping type is :person not "person" (keyword vs. string).
Your code esd/search has the same problem, but in addition you should change last_name to last-name and by lower casing "Smith" to "smith" everything should work.