I have a lazy-seq of maps and I'm attempting to remove maps from that lazy-seq based on the return value from another function. The other function will return true or false depending on whether or not a call of get returns a value equal to the parameter. The problem is the function isn't working correctly and I'm not too sure why.
(defn filter-by-name "Filter by names" [name m]
(if (= name (get m :name_of_person)) true false))
;To be called on each map
(defn remove-nonmatching-values "Remove anything not matching" [filter-val all-maps]
(map #(remove (filter-by-name filter-val %)) all-maps))
;trying to call on the lazy seq
You only need to call remove on the sequence of maps.
(defn remove-nonmatching-values
"Remove anything not matching"
[filter-val all-maps]
(remove #(filter-by-name filter-val %) all-maps))
Check Clojure's remove doc
(remove pred coll)
Returns a lazy sequence of the items in coll for which
(pred item) returns false. pred must be free of side-effects.
Returns a transducer when no collection is provided.
A function that produces the test-function you need for a given name is
(defn name-matcher [name]
(fn [m] (= name (:name_of_person m))))
All you have to do is filter the maps accordingly:
(defn retain-matching-maps [name maps]
(filter (name-matcher name) maps))
For example,
(retain-matching-maps "hello" (list {:name_of_person "hello"} {:name_of_person "bye"}))
;({:name_of_person "hello"})
I have got rid of
the comments (which are implied by the function names)
the if (as noted by Guillermo)
the get (Keywords - or maps - are implicit get functions)
the double negative in the function name remove-nonmatching-values.
You could also use :name instead of :name-of-person. The more succinctly you express your program, the less likely you are to make mistakes.
Related
I'm very new in Clojure. I'm learning with help from Clojure Koans. I found an answer with code below:
(= ["Real Jerry" "Bizarro Jerry"]
(do
(dosync
(ref-set the-world {})
(alter the-world assoc :jerry "Real Jerry")
(alter bizarro-world assoc :jerry "Bizarro Jerry")
(vec (map #(:jerry #%) [the-world bizarro-world]))))))
from: https://github.com/viebel/clojure-koans/blob/master/src/koans/16_refs.clj#L42
It's pretty unfriendly for Google to search like "Clojure #%". So I get nothing from Internet.
How does it works for the function "#(:jerry #%)"?
And the code below is the answer from me, but it doesn't work.
(= ["Real Jerry" "Bizarro Jerry"]
(do
(dosync
(ref-set the-world {})
(alter the-world assoc :jerry "Real Jerry")
(alter bizarro-world assoc :jerry "Bizarro Jerry")
(vec (map (fn [x] (:jerry x)) [the-world bizarro-world]))
)))
#( ...) is a reader macro for anonymous function where % means the first argument passed to the function. For example:
#(println %)
is equivalent to:
(fn [x] (println x))
# is a reader macro for deref so again:
#some-variable
is the same as:
(deref some-variable)
and is used to dereference a current value from one of the ref types.
Thus #(:jerry #%) is an anonymous function which when applied to a ref (e.g. an atom) will deref its current value and use it as an argument to call :jerry keyword as a function with the value.
the-world and bizarro-world are "derefable", which means that you can use # in front to get their value.
You are using an anonymous function, indicated by #( ). In an anonymous function, the percent sign % indicates the argument to the function.
So #% means, "dereference the argument to this function."
:jerry is a keyword used as a function, which gets the value associated with the key :jerry in the map.
For example:
(def coll [(ref {:jerry 21})
(ref {:jerry 42})])
=> #'user/coll
(map #(:jerry #%) coll)
=> (21 42)
Besides, you could find other "weird" symbols in clojure here .
https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful-characters-of-clojure/
I have a vector of maps like this one
(def map1
[{:name "name1"
:field "xxx"}
{:name "name2"
:requires {"element1" 1}}
{:name "name3"
:consumes {"element2" 1 "element3" 4}}])
I'm trying to define a functions that takes in a map like {"element1" 1 "element3" 6} (ie: with n fields, or {}) and fiters the maps in map1, returning only the ones that either have no requires and consumes, or have a lower number associated to them than the one associated with that key in the provided map (if the provided map doesn't have any key like that, it's not returned)
but I'm failing to grasp how to approach the maps recursive loop and filtering
(defn getV [node nodes]
(defn filterType [type nodes]
(filter (fn [x] (if (contains? x type)
false ; filter for key values here
true)) nodes))
(filterType :requires (filterType :consumes nodes)))
There's two ways to look at problems like this: from the outside in or from the inside out. Naming things carefully can really help when working with nested structures. For example, calling a vector of maps map1 may be adding to the confusion.
Starting from the outside, you need a predicate function for filtering the list. This function will take a map as a parameter and will be used by a filter function.
(defn comparisons [m]
...)
(filter comparisons map1)
I'm not sure I understand the comparisons precisely, but there seems to be at least two flavors. The first is looking for maps that do not have :requires or :consumes keys.
(defn no-requires-or-consumes [m]
...)
(defn all-keys-higher-than-values [m]
...)
(defn comparisons [m]
(some #(% m) [no-requires-or-consumes all-keys-higher-than-values]))
Then it's a matter of defining the individual comparison functions
(defn no-requires-or-consumes [m]
(and (not (:requires m)) (not (:consumes m))))
The second is more complicated. It operates on one or two inner maps but the behaviour is the same in both cases so the real implementation can be pushed down another level.
(defn all-keys-higher-than-values [m]
(every? keys-higher-than-values [(:requires m) (:consumes m)]))
The crux of the comparison is looking at the number in the key part of the map vs the value. Pushing the details down a level gives:
(defn keys-higher-than-values [m]
(every? #(>= (number-from-key %) (get m %)) (keys m)))
Note: I chose >= here so that the second entry in the sample data will pass.
That leaves only pulling the number of of key string. how to do that can be found at In Clojure how can I convert a String to a number?
(defn number-from-key [s]
(read-string (re-find #"\d+" s)))
Stringing all these together and running against the example data returns the first and second entries.
Putting everything together:
(defn no-requires-or-consumes [m]
(and (not (:requires m)) (not (:consumes m))))
(defn number-from-key [s]
(read-string (re-find #"\d+" s)))
(defn all-keys-higher-than-values [m]
(every? keys-higher-than-values [(:requires m) (:consumes m)]))
(defn keys-higher-than-values [m]
(every? #(>= (number-from-key %) (get m %)) (keys m)))
(defn comparisons [m]
(some #(% m) [no-requires-or-consumes all-keys-higher-than-values]))
(filter comparisons map1)
I have a function that is defined like this:
(defn delete-rule [rules rule]
(om/transact! rules
(fn [rules] (into [] (remove #(= rule %) rules)))))
What is the purpose of into here?
Wouldn't that produce exactly the same result as the one on the above:
(defn delete-rule [rules rule]
(om/transact! rules
(fn [rules] (remove #(= rule %) rules))))
From the documentation for remove:
Returns a lazy sequence of the items in coll for which (pred item)
returns false. pred must be free of side-effects. Returns a transducer
when no collection is provided.
From the documentation for into:
Returns a new coll consisting of to-coll with all of the items of
from-coll conjoined. A transducer may be supplied.
So the difference is, the delete-rule version with into returns a non-lazy vector, whereas the version without into returns a lazy sequence.
The particulars with your function is that om doesn't support lists (or lazy sequences) as cursors, only maps and vectors, that's why the output of remove must be converted to vector.
Hope that helps.
Is there a way to generically get metadata for arguments to a function in clojure? The answer posted in this question does not, actually, work in general:
user> (defn foo "informative dox!" [] 1)
#'user/foo
user> (defmacro get-docs [func] `(:doc (meta (var ~func))))
#'user/get-docs
user> (get-docs foo)
"informative dox!"
user> (get-docs (identity foo))
; Evaluation aborted.
user> (defn process-docs [f] (let [docs (get-docs f)] (reverse docs)))
; Evaluation aborted.
The second-to-last line doesn't work because you can't call var on the list (identity foo), and the last line doesn't even compile because the compiler complains about being unable to resolve f.
Most of the solutions for this problem I've found rely on the idea that you have access to the symbol in the function's definition, or something like that, so that you can do something like (resolve 'f) or (var f). But I want something that I can use on the argument to a function, where you don't know that information.
Essentially, I'd like an expression I can put in place of the question marks below to get the metadata of #'map:
(let [x map] (??? x))
its a mouthful though possible:
(let [x map]
(:doc (meta (second (first (filter #(and (var? (second %))
(= x (var-get (second %))))
(ns-map *ns*)))))))
produces the desired result:
"Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
under the hood Namespaces are essentially maps of names to vars and the vars contain functions. you can search the contents of these vars for the one that matches the function you are seeking and then look at it's associated var and get the metadata from that var.
I do not think the key-pres? function below is working the way I expect it to. First, here is the input data.
Data from which cmp-val derived:
["2" "000-00-0000" "TOAST" "FRENCH" "" "M" "26-Aug-99" "" "ALL CARE PLAN" "MEDICAL"]
Data that is missing the key (ssn).
["000-00-0000" "TOAST " "FRENCH " "RE-PART B - INSURED "]
The problem is, if I make one of the input's 000-00-0000 something else, I should
see that conj'd onto a log vector. I don't, and I don't see it printed with the if-not empty?.
(defn is-a-in-b
"This is a helper function that takes a value, a column index, and a
returned clojure-csv row (vector), and checks to see if that value
is present. Returns value or nil if not present."
[cmp-val col-idx csv-row]
(let [csv-row-val (nth csv-row col-idx nil)]
(if (= cmp-val csv-row-val)
cmp-val
nil)))
(defn key-pres?
"Accepts a value, like an index, and output from clojure-csv, and looks
to see if the value is in the sequence at the index. Given clojure-csv
returns a vector of vectors, will loop around until and if the value
is found."
[cmp-val cmp-idx csv-data]
(let [ret-rc
(for [csv-row csv-data
:let [rc (is-a-in-b cmp-val cmp-idx csv-row)]
:when (true? (is-a-in-b cmp-val cmp-idx csv-row))]
rc)]
(vec ret-rc)))
(defn test-key-inclusion
"Accepts csv-data file and an index, a second csv-data param and an index,
and searches the second csv-data instances' rows (at index) to see if
the first file's data is located in the second csv-data instance."
[csv-data1 pkey-idx1 csv-data2 pkey-idx2]
(reduce
(fn [out-log csv-row1]
(let [cmp-val (nth csv-row1 pkey-idx1 nil)]
(doseq [csv-row2 csv-data2]
(let [temp-rc (key-pres? cmp-val pkey-idx2 csv-row2)]
(if-not (empty? temp-rc)
(println cmp-val, " ", (nth csv-row2 pkey-idx2 nil), " ", temp-rc))
(if (nil? temp-rc)
(conj out-log cmp-val))))))
[]
csv-data1))
What I want the function to do is traverse data returned by clojure-csv (a vector of vectors). If cmp-val can be found at the cmp-idx location in csv-row, I'd like that
assigned to rc, and the loop to terminate.
How can I fix the for loop, and if not, what looping mechanism can I use to accomplish this?
Thank you.
you don't need true?, it specifically checks for the boolean true value;
don't repeat the call to is-a-in-b;
it would be more idiomatic (and readable) to use a-in-b? as the fn name;
I suggest simplifying the code, you don't really need that let.
Code:
(vec (for [csv-row csv-data
:let [rc (a-in-b? cmp-val cmp-idx csv-row)]
:when rc)]
rc))
But, this are just some general comments on code style... what you're implementing here is just a simple filter:
(vec (filter #(a-in-b? cmp-val cmp-idx %) csv-data))
Furthermore, this will return not only the first, but all matches. If I read your question right, you just need to find the first match? Then use some:
(some #(a-in-b? cmp-val cmp-idx %) csv-data)
UPDATE
Rereading your question I get the feeling that you consider for to be a loop construct. It's not -- it's a list comprehension, producing a lazy seq. To write a loop where you control when to iterate, you must use loop-recur. But in Clojure you'll almost never need to write you own loops, except for performance. In all other cases you compose higher-order functions from clojure.core.