What data structure to use for implementing a library management system - clojure

I'm new to functional programming and am currently at the end of "Getting Clojure" book. Since most of the examples in the book refer to books/library management, i thought of implementing a library management system as a project to help me learn and clear the concepts. I would like some advise on what would be the best data structure to hold the library - I'm thinking of either a vector of book maps or a map of book maps. Maps are easy to lookup. Vectors can be accessed quickly as well. I'm thinking of
{:ISBN {:title "Book title" :edition 1 :publisher "ABC publishing"}}
this way the lookup will be on ISBN number, but i would also like to lookup using book title ..but not sure how. Any help, suggestions, advise would be greatly appreciated.

You can use the index function in clojure.set:
(require '[clojure.set :as set])
(def library #{{:title "Book title" :edition 1 :publisher "ABC publishing" :isbn "1234"}
{:title "A title" :edition 2 :publisher "123 publishing" :isbn "4321"}})
(def by-isbn (set/index library [:isbn]))
(by-isbn {:isbn "1234"})
;; => #{{:title "Book title", :edition 1, :publisher "ABC publishing", :isbn "1234"}}
(def by-title (set/index library [:title]))
(by-title {:title "A title"})
;; => #{{:title "A title", :edition 2, :publisher "123 publishing", :isbn "4321"}}

In java you can use a data structure called Trie. it is better than Hashmaps and the time complexity of operations are O(1).
In this DS we can reuse the words like if we already have the word 'new' and you want to add another word 'news' It will it will just use the existing word new to make a new word. which stores storage.

Related

Meaning of # in clojure

In clojure you can create anonymous functions using #
eg
#(+ % 1)
is a function that takes in a parameter and adds 1 to it.
But we also have to use # for regex
eg
(clojure.string/split "hi, buddy" #",")
Are these two # related?
There are also sets #{}, fully qualified class name constructors #my.klass_or_type_or_record[:a :b :c], instants #inst "yyyy-mm-ddThh:mm:ss.fff+hh:mm" and some others.
They are related in a sence that in these cases # starts a sequence recognisible by clojure reader, which dispatches every such instance to an appropriate reader.There's a guide that expands on this.
I think this convention exists to reduce the number of different syntaxes to just one and thus simplify the reader.
The two uses have no (direct) relationship.
In Clojure, when you see the # symbol, it is a giant clue that you are "talking" to the Clojure Reader, not to the Clojure Compiler. See the full docs on the Reader here: https://clojure.org/reference/reader.
The Reader is responsible for converting plain text from a source file into a collection of data structures. For example, comparing Clojure to Java we have
; Clojure ; Java
"Hello" => new String( "Hello" )
and
[ "Goodbye" "cruel" "world!" ] ; Clojure vector of 3 strings
; Java ArrayList of 3 strings
var msg = new ArrayList<String>();
msg.add( "Goodbye" );
msg.add( "cruel" );
msg.add( "world!" );
Similarly, there are shortcuts that the Reader recognizes even within Clojure source code (before the compiler converts it to Java bytecode), just to save you some typing. These "Reader Macros" get converted from your "short form" source code into "standard Clojure" even before the Clojure compiler gets started. For example:
#my-atom => (deref my-atom) ; not using `#`
#'map => (var map)
#{ 1 2 3 } => (hash-set 1 2 3)
#_(launch-missiles 12.3 45.6) => `` ; i.e. "nothing"
#(+ 1 %) => (fn [x] (+ 1 x))
and so on. As the # or deref operator shows, not all Reader Macros use the # (hash/pound/octothorpe) symbol. Note that, even in the case of a vector literal:
[ "Goodbye" "cruel" "world!" ]
the Reader creates a result as if you had typed:
(vector "Goodbye" "cruel" "world!" )
Are these two # related?
No, they aren't. The # literal is used in different ways. Some of them you've already mentioned: these are an anonymous function and a regex pattern. Here are some more cases:
Prepending an expression with #_ just wipes it from the compiler as it has never been written. For example: #_(/ 0 0) will be ignored on reader level so none of the exception will appear.
Tagging primitives to coerce them to complex types, for example #inst "2019-03-09" will produce an instance of java.util.Date class. There are also #uuid and other built-in tags. You may register your own ones.
Tagging ordinary maps to coerce them to types maps, e.g. #project.models/User {:name "John" :age 42} will produce a map declared as (defrecord User ...).
Other Lisps have proper programmable readers, and consequently read macros. Clojure doesn't really have a programmable reader - users cannot easily add new read macros - but the Clojure system does internally use read macros. The # read macro is the dispatch macro, the character following the # being a key into a further read macro table.
So yes, the # does mean something; but it's so deep and geeky that you do not really need to know this.

Reasons not to use use a wildcard pull?

Are there any reasons not to use a wildcard pull?
(defn pull-wild
"Pulls all attributes of a single entity."
[db ent-id]
(d/pull db '[*] ent-id))
It's much more convenient than explicitly stating the attributes.
It depends on which attributes you need to have in your application and if it's data intensive or whether you want to pull lots of entities.
In case you use the client-library, you might want to minimize the data that needs to be send over the wire.
I guess there are lots of other thoughts about that.
But as long as it's fast enough I would pull the wildcard.
fricke
You may also be interested in the entity-map function from Tupelo Datomic. Given an EID (or a Lookup Ref) it will return the full record as a regular Clojure map:
(let [
; Retrieve James' attr-val pairs as a map. An entity can be referenced either by EID or by a
; LookupRef, which is a unique attribute-value pair expressed as a vector.
james-map (td/entity-map (live-db) james-eid) ; lookup by EID
james-map2 (td/entity-map (live-db) [:person/name "James Bond"] ) ; lookup by LookupRef
]
(is (= james-map james-map2
{:person/name "James Bond" :location "London" :weapon/type #{:weapon/wit :weapon/gun} } ))

Polymorphic Schemas in Clojure

I want to create polymorphic schemas/types, and I'm curious of best practices. The following 2 examples allow me to create a Frequency schema which can repeat an event monthly by day of month, or monthly by day of week (eg, every 15th, or every first monday, respectively).
The first one uses the experimental abstract map to accomplish this, and its syntax of it is awkward (IMO). Plus being in the experimental package concerns me a bit.
The second one uses s/conditional, and this suffers from not being able to easily coerce the value of type from a string to keyword, which is useful when dealing with a REST API, or JSON. (whereas s/eq is great for this).
In the general case, is one of these, or some third option, the best practice for conveying: Type A is one of Types #{B C D ...}?
Two options:
;;OPTION 1
​
(s/defschema Frequency (field (abstract-map/abstract-map-schema
:type {})
{}))
(abstract-map/extend-schema MonthlyByDOM Frequency
[:monthly-by-dom]
{:days #{MonthDay}})
(abstract-map/extend-schema MonthlyByDOW Frequency
[:monthly-by-dow]
{:days #{WeekDay}
:weeks #{(s/enum 1 2 3 4 5)}})
;;OPTION 2
​
(s/defschema MonthlyByDOM "monthly by day of month, eg every 13th and 21st day" {:type (s/eq :monthly-by-dom)
:days #{MonthDay}})
(s/defschema MonthlyByDOW "monthly by day of week, eg first, and third friday" {:type (s/eq :monthly-by-dow)
:days #{WeekDay}
:weeks #{(s/enum 1 2 3 4 5)}})
(s/defschema Frequency (field (s/conditional #(= (s/eq :monthly-by-dom) (do (prn %) (:type %))) MonthlyByDOM
#(= :monthly-by-dow (:type %)) MonthlyByDOW)
{:default {:type :monthly-by-dom
:days #{1 11 21}}}))
Similar questions that don't quite help:
https://groups.google.com/forum/#!topic/prismatic-plumbing/lMvazYXRAQQ
Polymorphic schema validation in Clojure
Validating multiple polymorphic values using Prismatic Schema

Recover map from the string representation

I have a string which is a representation of a clojure map. Is there an easy way to reconstruct the map from the string?
An example of the string -
{:Location {:CountryData {:country_cf 99, :country_code "us", :country "united states"}, :longitude -80.17833, :msa 33100, :dma 528}
Use read-string function:
(read-string "{:Location {:CountryData {:country_cf 99, :country_code \"us\", :country \"united states\"}, :longitude -80.17833, :msa 33100, :dma 528}}")
Very interesting. I've never used clojure, but in Lisp dialects this is done with the read function. I'll go out on a limb here and say you can do it in clojure according to http://clojure.org/reader.
So maybe (with-in-str your-string (read))?

In Clojure - How do I access keys in a vector of structs

I have the following vector of structs:
(defstruct #^{:doc "Basic structure for book information."}
book :title :authors :price)
(def #^{:doc "The top ten Amazon best sellers on 16 Mar 2010."}
best-sellers
[(struct book
"The Big Short"
["Michael Lewis"]
15.09)
(struct book
"The Help"
["Kathryn Stockett"]
9.50)
(struct book
"Change Your Prain, Change Your Body"
["Daniel G. Amen M.D."]
14.29)
(struct book
"Food Rules"
["Michael Pollan"]
5.00)
(struct book
"Courage and Consequence"
["Karl Rove"]
16.50)
(struct book
"A Patriot's History of the United States"
["Larry Schweikart","Michael Allen"]
12.00)
(struct book
"The 48 Laws of Power"
["Robert Greene"]
11.00)
(struct book
"The Five Thousand Year Leap"
["W. Cleon Skousen","James Michael Pratt","Carlos L Packard","Evan Frederickson"]
10.97)
(struct book
"Chelsea Chelsea Bang Bang"
["Chelsea Handler"]
14.03)
(struct book
"The Kind Diet"
["Alicia Silverstone","Neal D. Barnard M.D."]
16.00)])
I would like to sum the prices of all the books in the vector. What I have is the following:
(defn get-price
"Same as print-book but handling multiple authors on a single book"
[ {:keys [title authors price]} ]
price)
Then I:
(reduce + (map get-price best-sellers))
Is there a way of doing this without mapping the "get-price" function over the vector? Or is there an idiomatic way of approaching this problem?
Nice to see a Clojure 101-related question! :-)
You could map :price across best-sellers; it probably wouldn't make much of a difference as far as the degree to which this code is idiomatic is concerned. In more complex scenarios, using something like get-price might be better style and help maintainability.
As for possible more profound changes to the code, this is in fact the cleanest way to write it. One alternative would be to write a custom reduction function:
(reduce (fn [{price :price} result] (+ price result))
0
best-sellers)
This basically merges the map and the reduce together; occasionally this is useful, but in general, breaking down the transformation of a sequence into separate, well-defined steps helps readability & maintainability and should be the default way to go. Similar comments apply to all the other alternatives which come to my mind (including loop / recur).
All in all, I'd say you've nailed it. No tweaks to be made here. :-)