(def tables
[{:table "A" :occupied false :party nil}
{:table "B" :occupied false :party nil}
{:table "C" :occupied false :party nil}])
1) How do I make a change to a single map with a vector?
ex. set :occupied = true where :table= "C"
2) What about updating all map values?
ex. set :occupied = false for all maps
(map (fn [t] (if (= (:table t) "C") (assoc t :occupied true) t)) tables)
(map #(assoc % :occupied false) tables)
Note that these will not change your tables, because they are immutable data structures, this code will instead return a new sequence of tables with the differences you describe.
Related
This question already has answers here:
In Clojure, how to destructure all the keys of a map?
(3 answers)
Closed 4 years ago.
I have this sample code where I create vars by iterating over the key value pair of the map.
(defmacro block [bindings & body]
`(let [
~#(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `~v]) bindings) ]
~body))
(block {:a 1 :b 2} (if true (prn "true" a b) (prn "false")))
It works fine.
Ouput: "true" 1 2
But now I want to pass the same map as a var but, it thows an exception.
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol
(def ctx {:a 3 :b 4})
(block ctx (if true (prn "true" a b) (prn "false")))
The reason it doesn't work, when you pass a symbol that refers to a var that holds a map, is because the macro is only seeing the symbol literal — not the value it refers to. When you pass it a map literal, the macro is seeing the map literal.
If you wanted it to also support symbols, you'll need to resolve the var the symbol refers to and get its value e.g. (var-get (resolve bindings)):
(defmacro block [bindings & body]
`(let [~#(mapcat (fn [[k v]] [(if (symbol? k)
k
(symbol (name k))) `~v])
(cond
(map? bindings) bindings
(symbol? bindings) (var-get (resolve bindings))
:else (throw (Exception. "bindings must be map or symbol"))))]
~#body))
(block ctx (if true (prn "true" a b) (prn "false")))
"true" 3 4
(block {:a 3 :b 4} (if true (prn "true" a b) (prn "false")))
"true" 3 4
You also need to "splice" body into the form using ~#, because body will be a sequence of forms even if it only contains one form.
It can also help to look at how your macro expands when troubleshooting it:
(macroexpand-1 '(block ctx (if true (prn "true" a b) (prn "false"))))
=> (clojure.core/let [a 3 b 4] (if true (prn "true" a b) (prn "false")))
I have a vector of vectors that contains some strings and ints:
(def data [
["a" "title" "b" 1]
["c" "title" "d" 1]
["e" "title" "f" 2]
["g" "title" "h" 1]
])
I'm trying to iterate through the vector and return(?) any rows that contain a certain string e.g. "a". I tried implementing things like this:
(defn get-row [data]
(for [d [data]
:when (= (get-in d[0]) "a")] d
))
I'm quite new to Clojure, but I believe this is saying: For every element (vector) in 'data', if that vector contains "a", return it?
I know get-in needs 2 parameters, that part is where I'm unsure of what to do.
I have looked at answers like this and this but I don't really understand how they work. From what I can gather they're converting the vector to a map and doing the operations on that instead?
(filter #(some #{"a"} %) data)
It's a bit strange seeing the set #{"a"} but it works as a predicate function for some. Adding more entries to the set would be like a logical OR for it, i.e.
(filter #(some #{"a" "c"} %) data)
=> (["a" "title" "b" 1] ["c" "title" "d" 1])
ok you have error in your code
(defn get-row [data]
(for [d [data]
:when (= (get-in d[0]) "a")] d
))
the error is here:
(for [d [data] ...
to traverse all the elements you shouldn't enclose data in brackets, because this syntax is for creating vectors. Here you are trying to traverse a vector of one element. that is how it look like for clojure:
(for [d [[["a" "title" "b" 1]
["c" "title" "d" 1]
["e" "title" "f" 2]
["g" "title" "h" 1]]] ...
so, correct variant is:
(defn get-row [data]
(for [d data
:when (= "a" (get-in d [0]))]
d))
then, you could use clojure' destructuring for that:
(defn get-row [data]
(for [[f & _ :as d] data
:when (= f "a")]
d))
but more clojuric way is to use higher order functions:
(defn get-row [data]
(filter #(= (first %) "a") data))
that is about your code. But corretc variant is in other guys' answers, because here you are checking just first item.
(defn get-row [data]
(for [d data ; <-- fix: [data] would result
; in one iteration with d bound to data
:when (= (get-in d[0]) "a")]
d))
Observe that your algorithm returns rows where the first column is "a". This can e. g. be solved using some with a set as predicate function to scan the entire row.
(defn get-row [data]
(for [row data
:when (some #{"a"} row)]
row))
Even better than the currently selected answer, this would work:
(filter #(= "a" (% 0)) data)
The reason for this is because for the top answer you are searching all the indexes of the sub-vectors for your query, whereas you might only wantto look in the first index of each sub-vector (in this case, search through each position 0 for "a", before returning the whole sub-vector if true)
I have a function that is deduplicating with preference, I thought of implementing the solution in clojure using flambo function thus:
From the data set, using the group-by, to group duplicates (i.e based on a specified :key)
Given a :val as input, using a filter to check if the some of values for each row are equal to this :val
Use a map to untuple the duplicates to return single vectors (Not very sure if that is the right way though, I tried using a flat-map without any luck)
For a sample data-set
(def rdd
(f/parallelize sc [ ["Coke" "16" ""] ["Pepsi" "" "5"] ["Coke" "2" "3"] ["Coke" "" "36"] ["Pepsi" "" "34"] ["Pepsi" "25" "34"]]))
I tried this:
(defn dedup-rows
[rows input]
(let [{:keys [key-col col val]} input
result (-> rows
(f/group-by (f/fn [row]
(get row key-col)))
(f/values)
(f/map (f/fn [rows]
(if (= (count rows) 1)
rows
(filter (fn [row]
(let [col-val (get row col)
equal? (= col-val val)]
(if (not equal?)
true
false))) rows)))))]
result))
if I run this function thus:
(dedup-rows rdd {:key-col 0 :col 1 :val ""})
it produces
;=> [(["Pepsi" 25 34]), (["Coke" 16 ] ["Coke" 2 3])]]
I don't know what else to do to handle the result to produce a result of
;=> [["Pepsi" 25 34],["Coke" 16 ],["Coke" 2 3]]
I tried f/map f/untuple as the last form in the -> macro with no luck.
Any suggestions? I will really appreciate if there's another way to go about this.
Thanks.
PS: when grouped
;=> [[["Pepsi" "" 5], ["Pepsi" "" 34], ["Pepsi" 25 34]], [["Coke" 16 ""], ["Coke" 2 3], ["Coke" "" 36]]]
For each group, rows that have"" are considered duplicates and hence removed from the group.
Looking at the flambo readme, there is a flat-map function. This is slightly unfortunate naming because the Clojure equivalent is called mapcat. These functions take each map result - which must be a sequence - and concatenates them together. Another way to think about it is that it flattens the final sequence by one level.
I can't test this but I think you should replace your f/map with f/flat-map.
Going by #TheQuickBrownFox suggestion, I tried the following
(defn dedup-rows
[rows input]
(let [{:keys [key-col col val]} input
result (-> rows
(f/group-by (f/fn [row]
(get row key-col)))
(f/values)
(f/map (f/fn [rows]
(if (= (count rows) 1)
rows
(filter (fn [row]
(let [col-val (get row col)
equal? (= col-val val)]
(if (not equal?)
true
false))) rows)))
(f/flat-map (f/fn [row]
(mapcat vector row)))))]
result))
and seems to work
First of All, I have a Mysql table like this:
create table t (id int(11) PRIMARY KEY unsigned NOT NULL AUTO_INCREMENT, name varchar(20), age int(10));
I define a funtion that will create a row in t:
(require '[honeysql.core :as sql])
(defn do-something []
(sql/query {:insert-into :t
:values [{:name "name1" :age 10}]})
(> 3 (rand-int 5)))
And now I want to run this function until it return true but at most N times.
This take-timescode is wrong because repeat will eval the do-something function one time and then structure the lazy sequence.
(defn take-times []
(some true? (repeat 5 (do-something))))
This take-times2 will eval the do-something 5 times no matter what the do-something return.
(defn take-times2 []
(some true? (for [i (range 5)]
(do-something))))
What should I do if i do not use recursion function and macro?
This should work:
(->> (repeatedly do-something)
(take 5)
(some true?))
Update (04.11.2014):
Since repeatedly actually allows for an optional length parameter, this is also fine:
(some true? (repeatedly 5 do-something))
Example
(defn do-something
[]
;; 20% chance of true
(let [ret (rand-nth [true false false false false])]
(prn 'hello ret)
ret))
(defn run
[]
(->> (repeatedly do-something)
(take 5)
(some true?)))
(run)
;; hello false
;; hello false
;; hello true
;; => true
(run)
;; hello false
;; hello false
;; hello false
;; hello false
;; hello false
;; => nil
The reduce function has the reduced option to stop processing a sequence based on a user defined criteria.
Using the random sequence of true and false
(defn rand-seq [] (repeatedly #(rand-nth [true false false false false])))
Use reduce to build a vector until either a true is found or a maximum number of false values has been reached.
(defn at-most-or-true [s max-false]
(reduce (fn [acc v]
(if (and (not v) (< (count acc) max-false))
(conj acc v)
(reduced acc)))
[] s))
This can be tested by calling
(at-most-or-true (rand-seq) 5)
What are the concise/ elegant ways to put into a map key-value pairs for which the corresponding conditions are true?
That is to translate
[condition1 condition2 ...] [key1 val1 key2 val2 ...]
or
[condition1 condition2 ...] [key1 key2 ...] [val1 val2 ...]
or
[condition1 key1 val1 condition2 key2 val2...]
into
{key-for-true-condition1 val-for-true-condition1, key-for-true-condition2 val-for-true-condition2...}
I think to use "reduce" with "if" in its lambda but interested in more concise/ beautiful/ elegant/ idiomatic ways.
(into {} (for [[c k v] (partition 3 coll) :when c]
[k v]))
Based on the 'for'-Version from Kintaro but a little shorter.
To be honest IMO, the version with reduce and if are already the most elegant and idiomatic see comment from nickik below.
(def coll [true :a "v1" false :b "v2" true :c "v3"])
(reduce (fn [a [c k v]] (if c (assoc a k v) a)) {} (partition 3 coll))
Here is a version using the for comprehension for the third case:
(apply array-map (flatten (for [[c k v] (partition 3 coll) :when c]
[k v])))
Edit:
For the second case you convert it to the third case by doing:
(def c [true false true])
(def k [:a :b :c])
(def v ["v1" "v2" "v3"])
(def coll (interleave c k v))
But I think the map version from nickik is better here.
I would first think about it as how to be best map your functional operations over a stream:
Group condition/key/value into a chunk
Filter chunks where the condition is not true
Drop the conditions
Flatten the chunks
Create a map from the result
Which looks like:
(def coll [true :a "v1" false :b "v2" true :c "v3"])
(apply hash-map
(flatten
(map #(drop 1 %)
(filter #(first %)
(partition 3 coll)))))
Or if you're feeling thready:
(->> coll
(partition 3)
(filter #(first %))
(map #(drop 1 %))
flatten
(apply hash-map))
I'm not sure this is elegant or concise, but I think it's easy to read. Note that if you frequently deal with data in this shape, you may find that steps like (partition 3 coll) or (first %) might be useful reusable functions in their own right leading to something like:
(defn condition-group [coll] (partition 3 coll))
(defn condition [group] (first group))
(defn but-condition [group] (drop 1 group))
(defn kv-map [kv-pairs] (apply hash-map (flatten kv-pairs)))
(->> coll
condition-group
(filter condition)
(map but-condition)
kv-map)
(def coll [true :key1 "value1" false :key2 "value2" true :key3 "value3"])
(defn testpair [[cond key val]]
(when cond
{key val}))
(apply merge (map testpair (partition 3 coll)))
=> {:key3 "value3", :key1 "value1"}
This would be one way but if you want other combinations of condition key and value you have to change the code. You didn't mention witch one would be best.
Edit:
Because its the first on your list
(def conditions [true false true] )
(def keyval [:key1 "value1" :key2 "value2" :key3 "value3"])
(defn testpair [cond [key val]]
(when cond
{key val}))
(apply merge (map testpair conditions (partition 2 keyval)))
Because its fun :)
(def conditions [true false true] )
(def keys [:key1 :key2 :key3])
(def vals ["value1" "value1" "value3"])
(defn testpair [cond key val]
(when cond
{key val}))
(apply merge (map testpair conditions keys vals))