I have a Clojure Lazy Sequence:
{
{:keyOne 123, :keyTwo "TestVal"}
{:keyOne 456, :keyTwo "Value2"}
{:keyOne 789, :keyTwo "TestVal"}
}
I want to get the maps which have a specific value for a given key, e.g. I want all maps which have the value "TestVal" as the :keyTwo value, so I'd expect the first and third element in my result.
I assume I should be able to solve this using filter, but I've looked through all examples I could find and they never use such a nested structure.
{{:keyOne 123, :keyTwo "TestVal"}
{:keyOne 456, :keyTwo "Value2"}
{:keyOne 789, :keyTwo "TestVal"}}
In clojure, this expression doesn't make sense, this isn't the lazy sequence of maps.
To answer your question adequately,I think input data is like as below:
(def input '({:keyOne 123, :keyTwo "TestVal"}
{:keyOne 456, :keyTwo "Value2"}
{:keyOne 789, :keyTwo "TestVal"}))
We can make the expression for your purpose like this:
(filter (fn [m] (= "TestVal" (:keyTwo m))) input)
It doesn't care whether the input sequence is lazy or not-lazy(eager).
Related
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?
I am trying to retrieve data from a DB. The data is coming back in a lazy sequence. I can peek at the data in repl and it looks like this:
({:foo value1, :bar value2})
How can I get at this data? preferably, how can I turn it into a map? I've tried:
(doall sequence (get sequence :foo))
Which just returns nil. And
(apply hash-map user-settings)
which returns
llegalArgumentException No value supplied for key: {:foo value1, :bar value2} clojure.lang.PersistentHashMap.create (PersistentHashMap.java:77)
I am very new to clojure and have been stuck on this for way too long. Thanks in advance.
You already have a map, it just happens to be the only item in your list.
(def data (first '({:foo 123 :bar 456})))
(:foo data) ; => 123
Sometimes when you want to print lazy seq to see your data use into. For example if you want to see contents of a lazy vector use (into [] your-lazy-vector) or (into {} your-lazy-map).
You can do this uncool conversion within a println function or in a let. However, I recommend removing this kind of debugging aid before release or pull-request.
Lazy sequences are great, most of the time.
I'm reading data from a file where each line has two values. Each line is represented by a sequence within an outer sequence representing the file.
I'd like to restructure the data into a sequence of maps for further processing.
I know how to create a map from a key set and sequence of values:
=> (defstruct entry :name :age)
=> (apply struct entry '("John" 34))
{:name "John", :age 34}
But how do I create a sequence of such maps based on a sequence of value sequences?
(map (apply struct entry) '(("John" 34) ("Lisa" 41))))
results in:
java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.PersistentStructMap$Def
EDIT: Renamed symbol for clarity.
structs are obsolete, the preference is to use records now.
(defrecord Person [name age])
(map (partial apply ->Person) '(("John" 34) ("Lisa" 41)))
Use zipmap
(map (partial zipmap [:name :age]) '(("John" 34) ("Lisa" 41)))
;-> ({:name "John", :age 34} {:name "Lisa", :age 5})
I am learning clojure and trying to implement a problem. I am storing maps in a vector. Each map contains an id. For example [{:id 1 :name "abc"} {:id 2 :name "xyz"}]. The map also contains some more fields.
I read somewhere that, instead of using a vector to store the maps, I could use an array-map and do away with my id and store it something like {1 {:name "abc"}, 2 {:name "xyz"}}.
I tried going through the clojure docs but didn't find a good example to achieve this. Can some please help me out and give me a good example?
You can use assoc to add values to a map. assoc takes 3 args. The first arg is the map that you want to add to, 2nd arg is a key, and the third is a value. The function returns the old map with the key-value pair added.
Example:
(assoc {} 1 {:name "abc"})
returns
{1 {:name "abc"}}
Your idea is to lift the :id entry of each record-map into an index, while removing it from the map. You end up with a map of :id-less records instead of a vector of full records.
The following function lifts the key fk out of the collection of maps ms:
(defn key-by [fk ms]
(into {} (map (fn [m] [(get m fk) (dissoc m fk)]) ms)))
For example,
(key-by :id [{:id 1 :name "abc"} {:id 2 :name "xyz"}])
;{1 {:name "abc"}, 2 {:name "xyz"}}
Note:
Every record should have an :id.
Your :ids had better be distinct, or you'll lose records.
Don't depend on array-map: it's an implementation detail. A
modified version might well be a hash-map.
If you need your map sorted by key, use a sorted-map.
If you need to keep your records in insertion order, think again.
A still-learning clojure-newbie (me) got a list of maps.
Each map contains one account number and other information
(e.g. ({:account 123, :type "PK", :end "01.01.2013", ...} {:account 456 :type "GK" :end "01.07.2016", ...})
Now I need a function that sequentially puts a increasing number and the account number
( like {1, 123, 2, 456 etc}). And I didn't get it no matter what I tried.
I once learned Delphi, and it would be there like
for i :=1 to (count MYMAP)
do (put-in-a-list i AND i-th account number in the list)
inc i
Due to some restrictions I am not allowed to use functions out of the core and also I must not use "use", "ns", "require", "cycle", "time", "loop", "while", "defn", "defstruct", "defmacro", "def", "defn", "doall", "dorun", "eval", "read-string", "repeatedly", "repeat", "iterate", "import", "slurp", "spit" .
And - please excuse me if there is any bad english - it's not usual for me to ask such questions in english.
For lazy sequence of natural numbers interspersed with account numbers, you can try something like the following:
(interleave ; splices together the following sequences
(map inc (range)) ; an infinite sequence of numbers starting at 1
(map :account ; gets account numbers out of maps
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...])) ; your accounts
However, the {} notation in your example ({1, 123, 2, 456 etc}) suggests you might be more interested in a map. In that case, you can use zipmap:
(zipmap ; makes a map with keys from first sequence to values from the second
(map inc (range))
(map :account
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...]))
map-indexed will help you create an increasing number sequence:
user> (let [f (comp (partial into {})
(partial map-indexed #(vector (inc %) (:account %2))))]
(f [{:account 123, :type "PK", :end "01.01.2013"} {:account 456 :type "GK" :end "01.07.2016"}]))
{1 123, 2 456}