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