Is there a Clojure function for ordinal indicators? - clojure

I want a function that will populate the contents of this map:
{:1 "first" :2 "second" :3 "third" :4 "fourth" ... :100 "one-hundredth" ...}
So I can do something similar to
(println "This is the " (:3 {... :3 "third" ...}) " item in the sequence")
> This is the third item in the sequence
Is there an existing Clojure library that does this?
My question is: Is there a Clojure function for ordinal indicators?
(If there is a better way to describe this kind function - please let me know)

user=> (require '[clojure.pprint :as pprint])
nil
user=> (map #(pprint/cl-format nil "~:R" %) [1 2 3 4 100])
("first" "second" "third" "fourth" "one hundredth")
note that :1 is not 1, and : is not a syntax for map keys
user=> (pprint/pprint (into {} (map (fn [n] [n (pprint/cl-format nil "~:R" n)]) (range 20))))
{0 "zeroth",
7 "seventh",
1 "first",
4 "fourth",
15 "fifteenth",
13 "thirteenth",
6 "sixth",
17 "seventeenth",
3 "third",
12 "twelfth",
2 "second",
19 "nineteenth",
11 "eleventh",
9 "ninth",
5 "fifth",
14 "fourteenth",
16 "sixteenth",
10 "tenth",
18 "eighteenth",
8 "eighth"}
nil

Related

Iterate a map in Clojure

I have some data as seen bellow.
(def my-data
{1 {:x 63 :y 14 :z [30 26]}
2 {:x 22 :y 15 :z [32 66]}
3 {:x 24 :y 16 :z [38 40]}})
I want to iterate through my-data to get the result bellow:
1 2 3
This is what I have done so far
(println (-> (seq my-data)(ffirst ,,,)
and my result
1
If you want to print each key in a different line, you can use either of these:
(doseq [item (keys my-data)]
(println item))
(run! println (keys my-data))
You just go:
(println (keys my-data))
The non-popular answer: this will not work for bigger maps. Small maps are ordered (insert order), but larger maps switch types and are no longer ordered/sorted. So "first" no longer makes sense.
user=> (type (zipmap (range 8) (range 8)))
; => #<Class#34f7234e clojure.lang.PersistentArrayMap>
user=> (take 8 (keys (zipmap (range 8) (range 8))))
; => (0 1 2 3 4 5 6 7)
user=> (type (zipmap (range 9) (range 9)))
; => #<Class#45dd4eda clojure.lang.PersistentHashMap>
user=> (take 8 (keys (zipmap (range 9) (range 9))))
; => (0 7 1 4 6 3 2 5)
The other answers are correct for larger maps, if you you it via sorted-map or sorted-map-by, if sorting your keys solves the problem. If you need the order you might be better off using vectors of vectors.

lang.LazySeq cannot be cast to IPersistantVector

In the process of learning Clojure.
I have a function to draw a random card from the deck
(defn draw-random-card
[cards]
(let [card (rand-nth cards)
index (.indexOf cards card)]
{:card card :remaining-cards (concat (subvec cards 0 index)
(subvec cards (inc index)))}))
Running it:
(draw-random-card ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
=> {:card 4, :remaining-cards ("Ace" 2 3 5 6 7 8 9 10 "Jack" "Queen" "King")}
I'd like to call it twice and get 2 cards out but the second time it calls it, it will pass the reduced deck from the first call.
IN the end I'd like to have the 2 cards and the reduced deck to use later.
I would have thought I could do something like:
(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
(let [first-draw (draw-random-card full-deck)
first-card (:drawn-card first-draw)
second-draw (draw-random-card (:remaining-cards first-draw))
second-card (:drawn-card second-draw)
remaining-deck (:remaining-cards second-draw)]
(println "First card: " first-card)
(println "Second card: " second-card)
(println "Remaining deck:" remaining-deck))
However, I'm obviously doing something dumb here as I get the error:
Execution error (ClassCastException) at aceyducey.core/draw-random-card (form-init3789790823166246683.clj:5).
clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentVector
I think the problem is in the line
second-draw (draw-random-card (:remaining-cards first-draw))]
Because remaining-cards isn't a vector?
Which means
concat (subvec cards 0 index)
(subvec cards (inc index)))}))
Isn't returning a vector? Rather a lazy sequence ???
But at this point I'm lost.
Help!
#amalloy makes a good point: the Clojure built-in function shuffle is probably what you want:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test) )
(def cards [:ace 2 3 4 5 6 7 8 9 10 :jack :queen :king] )
(dotest
(dotimes [i 3]
(spyx (shuffle cards))))
=>
Testing tst.demo.core
(shuffle cards) => [:king :jack 6 2 9 10 :ace 4 8 5 3 :queen 7]
(shuffle cards) => [2 :jack 7 9 :queen 8 5 3 4 :ace 10 :king 6]
(shuffle cards) => [7 :queen :jack 4 3 :king 6 :ace 2 10 5 8 9]
This and much more is available at the Clojure CheatSheet. Be sure to bookmark it and always keep a browser tab open with it.
concat returns a lazy sequence. You can coerce it into a vector by using:
(vec (concat ...))
Here is the full code with a test:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn draw-random-card
[cards]
(let [card (rand-nth cards)
index (.indexOf cards card)]
{:drawn-card card :remaining-cards (vec (concat (subvec cards 0 index)
(subvec cards (inc index))))}))
(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
(dotest
(let [first-draw (draw-random-card full-deck)
first-card (:drawn-card first-draw)
second-draw (draw-random-card (:remaining-cards first-draw))
second-card (:drawn-card second-draw)
remaining-deck (:remaining-cards second-draw)]
(println "First card: " first-card)
(println "Second card: " second-card)
(println "Remaining deck:" remaining-deck))
)
and result:
-------------------------------
Clojure 1.10.0 Java 12
-------------------------------
Testing tst.demo.core
First card: Queen
Second card: King
Remaining deck: [Ace 2 3 4 5 6 7 8 9 10 Jack]
Update:
To be specific, the problem was the call to subvec in the 2nd iteration of your code. Here is an example:
(dotest
(let [vals (vec (range 10)) ; a vector
s1 (subvec vals 2 4) ; so `subvec` works
s2 (subvec vals 6) ; and again
lazies (concat s1 s2)] ; creates a lazy sez
(is= [2 3] (spyxx s1))
(is= [6 7 8 9] (spyxx s2))
(is= [2 3 6 7 8 9] (spyxx lazies))
(throws? (subvec lazies 0 2)))) ; ***** can't call `subvec` on a non-vector (lazy sequence here) *****
with result:
s1 => <#clojure.lang.APersistentVector$SubVector [2 3]>
s2 => <#clojure.lang.APersistentVector$SubVector [6 7 8 9]>
lazies => <#clojure.lang.LazySeq (2 3 6 7 8 9)>
so by coercing the output of concat to a vector, the call to subvec succeeds on the next time through the function.
So, in hindsight, a better solution would have been to coerce the input to a vector like so:
(let [cards (vec cards)
card (rand-nth cards)
index (.indexOf cards card)]
{:drawn-card card
:remaining-cards (vec (concat (subvec cards 0 index)
(subvec cards (inc index))))}))
Update #2
If you don't want to coerce your input to a vector, you can use the .subList() function via Java interop:
(dotest
(spyxx (.subList (concat (range 5) (range 10 15)) 5 10))
(spyxx (.subList (range 10) 2 5))
(spyxx (.subList (vec (range 10)) 2 5))
(spyxx (subvec (vec (range 10)) 2 5))
(throws? (subvec (range 10) 2 5))) ; *** not allowed ***
with result
(.subList (concat (range 5) (range 10 15)) 5 10)
=> <#java.util.ArrayList$SubList [10 11 12 13 14]>
(.subList (range 10) 2 5)
=> <#java.util.Collections$UnmodifiableRandomAccessList [2 3 4]>
(.subList (vec (range 10)) 2 5)
=> <#clojure.lang.APersistentVector$SubVector [2 3 4]>
(subvec (vec (range 10)) 2 5)
=> <#clojure.lang.APersistentVector$SubVector [2 3 4]>

How to read a file with test data in with Clojure?

I am writing a piece of code that needs to read in a text file that has data. The text file is in the format:
name 1 4
name 2 4 5
name 3 1 9
I am trying to create a vector of a map in the form [:name Sarah :weight 1 cost :4].
When I try reading the file in with the line-seq reader, it reads each line as an item so the partition is not correct. See repl below:
(let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 3 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight jason 4 5, :value nila 3 2 } {:name jonas 5 6, :weight judy 8 15, :value denny 9 14}]
I then tried to just take 1 partition, but still the structure is not right.
=> (let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 1 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight nil, :value nil} {:name jason 4 5, :weight nil, :value nil} {:name nila 3 2 , :weight nil, :value nil} {:name jonas 5 6, :weight nil, :value nil} {:name judy 8 15, :weight nil, :value nil} {:name denny 9 14, :weight nil, :value nil} {:name lis 2 2, :weight nil, :value nil} {:name , :weight nil, :value nil}]
nil
Next I tried to slurp the file, but that is worse:
=> (let [slurp-input (slurp "C://Drugs/myproject/src/myproject/data.txt")
part-items (partition 3 slurp-input)
mapping (vec (map #(apply struct item %) part-items))]
(println slurp-input)
(println part-items)
(println mapping))
sarah 1 1
jason 4 5
nila 3 2
jonas 5 6
judy 8 15
denny 9 14
lis 2 2
((s a r) (a h ) (1 1) (
Please help! This seems like such an easy thing to do in Java, but is killing me in Clojure.
split it into a sequence of lines:
(line-seq (reader "/tmp/data"))
split each of them into a sequence of words
(map #(split % #" ") data)
make a function that takes a vector of one data and turns it into a map with the correct keys
(fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
then nest them back together
(map (fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
(map #(split % #" ") (line-seq (reader "/tmp/data"))))
({:weight 1, :name "name", :cost 4}
{:weight 2, :name "name", :cost 4}
{:weight 3, :name "name", :cost 1})
you can also make this more compact by using zip-map
You are trying to do everything in one place without testing intermediate results. Instead Clojure recommends to decompose task into a number of subtasks - this makes code much more flexible and testable. Here's the code for your task (I assume records in file describe people):
(defn read-lines [filename]
(with-open [rdr (clojure.java.io/reader filename)]
(doall (line-seq rdr))))
(defn make-person [s]
(reduce conj (map hash-map [:name :weight :value] (.split s " "))))
(map make-person (read-lines "/path/to/file"))

How To Generate Multiple Calls To Function With One Arg In Sequence

I want to take a sequence [44 1 11] generated using (map #(nth %1 0 nil) v1) and feed (map) that into successive calls to the same function. I am just not sure which Clojure builtin or builtins to use other than for.
Here are the details.
Given these two vectors:
(def v1 [[44 2 3 4 5]
[1 6 7 5 10]
[11 12 13 14 15]])
(def v2 [[1 2 3 4 44]
[1 6 7 5 1]
[11 12 13 14 44]])
and this function
(defn ret-non-match-rows
"Expects a sequence of sequences, like what is returned from clojure-csv.
Returns all csv rows that do not match cmp-val at cmp-col-idx."
[s-o-s cmp-val cmp-col-idx]
(filter (complement nil?)
(map #(if (ret-col-match %1 cmp-val cmp-col-idx) nil %1) s-o-s) ))
So I am asking for help in how to feed (map) [44 1 11] into ret-non-match-rows like this
(ret-non-match-rows v2 44 4)
(ret-non-match-rows v2 44 1)
(ret-non-match-rows v2 44 11)
but using Clojure built-ins to generate those individual calls.
Thank You.
Edit:
The following gives me what I want, but I'm wondering if there is a cleaner way to do it.
(def ssn-1 [44 1 11])
(def tst (partial ret-non-match-rows v2 4))
(map #(tst %1) ssn-1)
I get back a sequence of sequences and will parse that to get my results.
Maybe you want this:
(map (partial ret-non-match-rows v2 44) (map first v1))
(assuming the 4 in the first example call is a typo and should be 44)

return sequence of clojure map values in a specific order

If I have a map, for example,
(def mymap { :b 1 :a 2 :d 3 :e 4 :f 5})
I can use vals to get a sequence of all of the values
(vals mymap)
;=> (1 2 3 4 5)
how do I get the sequence of values in my own custom order, to get for example
;=> (4 2 3 1 5)
what I eventually want to do is serialize the values to a string, doing something like this
(defn serialize [m sep] (apply str (concat (interpose sep (vals m)) ["\n"])))
(this example function was taken from the "serialize an input-map into string" post)
but I need to specify the order of the vals.
Maps are functions of their keys, so you can do this:
(map mymap [:e :a :d :b :f])
=> (4 2 3 1 5)
For 1.3 you can use the priority-map,
http://clojure.github.com/clojure-contrib/branch-master/priority-map-api.html
or you can use sort-by,
(let [m { 1 8 3 6 5 4 7 2}]
(println (map first (sort-by second m)))
(println (map first (sort-by first m))))
(7 5 3 1)
(1 3 5 7)
In case you want to sort the map depending on the keys, and then get the values,
Brian has an example on how to do this using sort-by
Or you can just implement your own sort comparator
I don't want to sort (although thanks for the sorting tips), I just want to specify the order
when I pull the values from the map.
I found a way to do it - destructuring the map.
(let [{:keys [a b d e f]} mymap]
(println e a d b f))