Display column names in table - clojure

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"}])))

Related

Finding a regular-expression-like sequence of values in a Clojure vector

I am using the libpostal library to find an full address (street, city, state, and postal code) within a news article. libpostal when given input text:
There was an accident at 5 Main Street Boulder, CO 10566 -- which is at the corner of Wilson.
returns a vector:
[{:label "house", :value "there was an accident at 5"}
{:label "road", :value "main street"}
{:label "city", :value "boulder"}
{:label "state", :value "co"}
{:label "postcode", :value "10566"}
{:label "road", :value "which is at the corner of wilson."}
I am wondering if there is a clever way in Clojure to extract a sequence where the :label values occur in a sequence:
[road unit? level? po_box? city state postcode? country?]
where ? represents an optional value in the match.
You could do this with clojure.spec. First define some specs that match your maps' :label values:
(defn has-label? [m label] (= label (:label m)))
(s/def ::city #(has-label? % "city"))
(s/def ::postcode #(has-label? % "postcode"))
(s/def ::state #(has-label? % "state"))
(s/def ::house #(has-label? % "house"))
(s/def ::road #(has-label? % "road"))
Then define a regex spec e.g. s/cat + s/?:
(s/def ::valid-seq
(s/cat :road ::road
:city (s/? ::city) ;; ? = zero or once
:state ::state
:zip (s/? ::postcode)))
Now you can conform or valid?-ate your sequences:
(s/conform ::valid-seq [{:label "road" :value "Damen"}
{:label "city" :value "Chicago"}
{:label "state" :value "IL"}])
=>
{:road {:label "road", :value "Damen"},
:city {:label "city", :value "Chicago"},
:state {:label "state", :value "IL"}}
;; this is also valid, missing an optional value in the middle
(s/conform ::valid-seq [{:label "road" :value "Damen"}
{:label "state" :value "IL"}
{:label "postcode" :value "60622"}])
=>
{:road {:label "road", :value "Damen"},
:state {:label "state", :value "IL"},
:zip {:label "postcode", :value "60622"}}

Clojure: Update value of record field

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

Custom equality in Clojure distinct

In a Clojure program I've an array composed by maps containing peoples' names and emails.
e.g.
[
{ :name "John" :email "john#gmail.com" }
{ :name "Batman" :email "batman#gmail.com" }
{ :name "John Doe" :email "john#gmail.com" }
]
I'd like to remove the duplicate entries considering, for comparison purposes, pairs having the same e-mail to be equals. In the example above the output would be:
[
{ :name "John" :email "john#gmail.com" }
{ :name "Batman" :email "batman#gmail.com" }
]
What's the best way to achieve this in Clojure? Is there a way to let distinct knows what equals function to use?
Thanks.
yet another way to do it, kinda more idiomatic, i guess:
(let [items [{ :name "John" :email "john#gmail.com" }
{ :name "Batman" :email "batman#gmail.com" }
{ :name "John Doe" :email "john#gmail.com" }]]
(map first (vals (group-by :email items))))
output:
({:name "John", :email "john#gmail.com"}
{:name "Batman", :email "batman#gmail.com"})
that is how it works:
(group-by :email items) makes a map, whose keys are emails, and values are groups of records with this email
{"john#gmail.com" [{:name "John", :email "john#gmail.com"}
{:name "John Doe", :email "john#gmail.com"}],
"batman#gmail.com" [{:name "Batman", :email "batman#gmail.com"}]}
then you just need to take its vals (groups of records) and select firsts from them.
And another way is to create a sorted set by email, so it will treat all the records with equal emails as equal records:
(let [items [{ :name "John" :email "john#gmail.com" }
{ :name "Batman" :email "batman#gmail.com" }
{ :name "John Doe" :email "john#gmail.com" }]]
(into (sorted-set-by #(compare (:email %1) (:email %2))) items))
output:
#{{:name "Batman", :email "batman#gmail.com"}
{:name "John", :email "john#gmail.com"}}
don't really know which of them is more idiomatic and has a better performance. But i bet on the first one.
This would do it: https://crossclj.info/fun/medley.core/distinct-by.html.
The function in the link goes through every value lazily and stores everything it's seen. If the value in the coll is already seen, it does not add it.
You could then call this as: (distinct-by #(% :email) maps), where maps is your vector of people-maps.
A distinct-by can easily be implemented as
(defn distinct-by [f coll]
(let [groups (group-by f coll)]
(map #(first (groups %)) (distinct (map f coll)))))
For the example case this can be used like
(distinct-by :email
[{:name "John" :email "john#gmail.com"}
{:name "Batman" :email "batman#gmail.com"}
{:name "John Doe" :email "john#gmail.com"}])

Generating structured maps with test.check

I'm playing around with test.check, and I'm testing a function which takes a map as an argument. These maps do have a defined structure, such as:
{:name "Bob" :age 42 :email "bob#example.com" :admin true}
Key point, there is a set of expected keys, the values of which have differing clearly defined generators.
I took a look at gen/map, but it's not obvious how to use it for more structured key/value pairs:
(gen/sample (gen/map gen/keyword gen/boolean) 5)
;; => ({} {:z false} {:k true} {:v8Z false} {:9E false, :3uww false, :2s true})
This seems like a simple scenario, but I can't find an example.
How can I generate structured maps, such as the one described here, using test.check?
Use gen/hash-map instead of gen/map.
=> (gen/sample (gen/hash-map :name gen/string
:age gen/int
:email email-gen ; from test.check examples
:admin gen/boolean))
({:email "00w#hotmail.com", :age 0, :name "", :admin true}
{:email "mi6#computer.org", :age -1, :name "Á6", :admin false}
{:email "Ux4gp#hotmail.com", :age 4, :name "z", :admin true})

clojure rename-keys in nested structure

Suppose I have a nested structure, something like this:
{:data1
{:categories [
{:name "abc" :id 234 :desc "whatever"}
{:name "def" :id 456 :desc "nothing"}]
}
:data2 {...}
:data3 {...}
}
And I need to transform the key names in the maps. I can transform the top level keys like this:
(rename-keys mymap {:data1 :d1})
But I'm not sure how to rename keys nested more deeply in the data structure (say I want to rename the :desc field to :description).
I'm pretty sure that zippers are the answer but can't quite figure out how to do it, or if there's a more straightforward way.
Same as Brian Carper's solution, except the walk namespace already has a specific function for this purpose. All keys at all levels are changed, be they nested inside any sort of collection or seq.
(:use 'clojure.walk)
(def x
{:data1
{:categories
[{:desc "whatever", :name "abc", :id 234}
{:desc "nothing", :name "def", :id 456}]},
:data2
{:categories
[{:desc "whatever", :name "abc", :id 234}
{:desc "nothing", :name "def", :id 456}]}})
(postwalk-replace {:desc :something} x)
{:data1
{:categories
[{:something "whatever", :name "abc", :id 234}
{:something "nothing", :name "def", :id 456}]},
:data2
{:categories
[{:something "whatever", :name "abc", :id 234}
{:something "nothing", :name "def", :id 456}]}}
postwalk is a pretty heavy sledgehammer in general, although it looks from your original question like you might need it. In many cases, you can perform updates in a nested structure with update-in:
user> (let [m {:foo {:deep {:bar 1 :baz 2}}}]
(update-in m [:foo :deep] clojure.set/rename-keys {:baz :periwinkle}))
{:foo {:deep {:periwinkle 2, :bar 1}}}
If you want to rename all :desc keys regardless of at which level of nesting they're located, this might work. If you only want to rename :desc keys at a certain level of nesting, you'll need something slightly more sophisticated.
This only works because clojure.set/rename-keys currently does nothing (returns its first argument untouched) if its first argument isn't a map.
user> (require '[clojure [set :as set] [walk :as walk]])
nil
user> (def x {:data1
{:categories
[{:desc "whatever", :name "abc", :id 234}
{:desc "nothing", :name "def", :id 456}]},
:data2
{:categories
[{:desc "whatever", :name "abc", :id 234}
{:desc "nothing", :name "def", :id 456}]}})
#'user/x
user> (walk/postwalk #(set/rename-keys % {:desc :description :id :ID}) x)
{:data1
{:categories
[{:name "abc", :ID 234, :description "whatever"}
{:name "def", :ID 456, :description "nothing"}]},
:data2
{:categories
[{:name "abc", :ID 234, :description "whatever"}
{:name "def", :ID 456, :description "nothing"}]}}
nil