Clojure: easily convert from invalid string to valid keyword - clojure

I've been trying to find an idiomatic way to convert user data to valid keywords in clojure.
A possible use case for this is when reading in an excel spreadsheet, I would like to dynamically build a map for each row besides the first where the first row contains headers that will be keywords . I need to account for the headers possibly containing spaces or other invalid characters. I have read that the keyword function will not complaim and will give you an invalid key that may be hard to work with or even harmful.
I could manually make the conversions or possibly use a framework like slugger to do this, but i wanted to know if there was anything already built-in that could handle this.
Also, I have read that at one point creating too many keys could overload the heap, but that was from 2010 and it may have been resolved in 1.3. Would it just be best for me to create my hash-map with string key instead of keywords? I have read that doing so is not idiomatic.

Unless you have good reason for doing otherwise, simply use the string itself as a key.
user=> (def my-db (atom {}))
#'user/my-db
user=> (swap! my-db assoc "New York" 1)
{"New York" 1}
user=> (swap! my-db assoc "Los Angeles" 2)
{"Los Angeles" 2, "New York" 1}
user=> (do (print "Which city do you want to rank?\n =>")
(flush)
(#my-db (read-line)))
Which city do you want to rank?
=>New York
1
If you encode/keywordize your map, you'll instead have to encode/keywordize or stringify/decode on each interaction with the user's conventions.

Hm, it does appear that keyword will spit out unreadable keywords:
user=> (keyword "foo bar")
:foo bar
user=> (keyword "foo:")
:foo:
Neither of these can be read in again.
I would just write a small function to clean your input (rules here) before passing it to the keyword function.

Related

Why is the hash map get returning nil after passing through the hash map as a function argument?

I am very new to clojure so this may have a simple fix. I have two examples of code that I can't seem to find the difference in why one works and the other doesn't. The first is:
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(println demokeys)
(println (get demokeys "b")))
(example)
which is from https://www.tutorialspoint.com/clojure/clojure_maps_get.htm. This works the way I expect it to, with it printing the hash map and then a 2 on the next line.
The second example is:
(defn bill-total [bill]
(println bill)
(println (get bill "b"))
(println "sometext")
)
(bill-total[(hash-map "z" 1 "b" 2 "a" 3)])
which prints the hashmap, then nil, then sometext. Why does it not correctly print the 2 as it does in the previous example?
First of all, don't use def within defn unless you need dynamic runtime declarations (and even then, you should probably reconsider). Instead, use let for local bindings.
As to your main question, you have wrapped the map in a vector, with those [] here: (bill-total [...]). When calling a function, you don't need to repeat its arguments vector's brackets - call it just like you call println or hash-map, because they're also just regular functions.
So in the end, it should be (bill-total (hash-map "z" 1 "b" 2 "a" 3)).
As a final note, there's rarely a need to explicitly use hash-map, especially when you already know the keys and values. So, instead of (hash-map ...) you can use the map literal and write {...}, just like in {"z" 1, "b" 2, "a" 3} (I used commas here just for readability since Clojure ignores them).

assoc with argument clojure

If I have an hash-map and I want to assoc a value to it, and I get the key as an argument, what should i do?
(defn define [name type kind] "define new var in one of the tables"
(if (or (= type "static") (= type "field"))
(def classScope (assoc classScope name (list type kind (addCount kind))))
(def methodScope (assoc methodScope name (list type kind (addCount kind))))
)
)
My problem is that i can't use :name, and not 'name.
Thanks!!
Update: If you want your keys to be in keyword form, just call keyword on them....
(defn my-map-fn [name type kind]
(assoc some-map (keyword name) (some-fn type kind)))
e.g.
(my-map-fn "some-name" "some-type" "some-kind") => {:some-name some-val}
Note that you shouldn't use def inside of defn. It looks like you want to keep a map of data and as you call define you want to store some more data in that map. A way that I go about this is to use atoms (there are other ways too).
(defonce classScope (atom {})
(defonce methodScope (atom {}))
(defn define
"if you want a doc string it goes here"
[name type kind]
(swap! (if (#{"static" "field"} type) classScope methodScope)
#(assoc % name (list type kind (addCount kind)))))
the benefit here is you get atomic updates and calls to define that may happen really close together won't butt heads.
Let's start with your explanatory comment:
I'm trying to create an hash-map that's like a symbol table: I will
identify each variable by its name, and it will have a list with its
type, kind and index in the code.
A few thoughts:
Don't use a list for the variable's characteristics; use a map.
You can think of the name of a variable as
a plain old string
a symbol
a keyword
Any of these works as the key of a map entry. Keep it simple. Use a string.
You're going to need such a table for every scope. And a scope should know its enclosing scope.
The descriptors static and field are not types; nor are they
alternatives in - say - Java.
I suggest you look at clojure.spec and typed Clojure to see how similar problems are handled within Clojure.

clojure - trouble destructing map inside macro

I'm a newbie in clojure, so please bear with me.
Writing a macro as so:
`(let [query# (:query-params ~'+compojure-api-request+)
options# (select-keys query# [:sort-by :from :to])])
First line of the let block destructures a query-params from http request - which produces this structure:
{sort-by billing-account/name, from 0, to 10, payment-due , payment-method , search }
And the trouble is with the second line - it returns an empty map when I use select-keys, however when I say for example (first query#) - the output looks like this: [sort-by billing-account/name]
Could anyone please explain why the select-keys does not work?
P.S. Tried (get query# :from) & (:from query#) - no luck there as well.
UPD
Keys were strings, not keywords - therefore using strings as keys works just fine.
By the way, you can also destructure string keys with :strs:
(let [m {"sort-by" "billing-account/name",
"from" "0",
"to" "10",
"payment-due" nil,
"payment-method", "search"}
{:strs [sort-by from to payment-due payment-method]} m]
(println sort-by from to payment-due payment-method))
;;=> billing-account/name 0 10 nil search
See https://clojure.org/guides/destructuring for a full description of the destructuring syntax.
I think you are confused by the differences between keywords, symbols and strings. In your comment you say that they're symbols, but in your edit you say they're strings.
You should read up on the difference:
in Clojure, why have Strings, Keywords AND Symbols?
Why does Clojure have "keywords" in addition to "symbols"?
The idiomatic thing is to usually prefer using keywords as map keys, although stuff that comes from the internet (json, http headers, etc) is sometimes all strings.
To answer your question directly, the keys passed to select-keys need to be equal (using the = function) to the ones in the map, so in this case they need to be the same type.
;; For example
(select-keys {'foo 1 'bar 2} ['foo]) ;=> {foo 1}
(select-keys {:foo 1 :bar 2} [:foo]) ;=> {:foo 1}
(select-keys {"foo" 1 "bar" 2} ["foo"]) ;=> {"foo" 1}
Also I question the need for this to be a macro, is there a reason that a plain function won't work?

How to iterate and merge a function results in clojure?

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))

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?