How does an alias passed to -M
look like? is its value a vector of strings? (in deps.edn)
@kaxaw75836 -M:foo:bar:quux
will look for the three separate aliases :foo
, :bar
, and :quux
in the combined deps.edn
data (system + user + project), and then the contents of those three aliases will be merged (according to the rules explained in the Clojure CLI docs on http://clojure.org -- some are merged as hash maps, some are concatenated as vectors, some are "last one wins").
I'm not sure where "vector of strings" comes in? The aliases are all read and merged before your program is run.
With the latest CLI, programs started via -M
or -X
can read the system property that provides the location of an EDN file that you can read to get a full hash map that contains the combined alias data, the lib path data, etc -- the "basis".
:aliases {:dev {:extra-paths ["dev"]}
:nrepl {:extra-deps {cider/cider-nrepl {:mvn/version "0.25.3"}}}
:nrepl-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}
I'm doing clojure -A:dev:nrepl -M:nrepl-opts
something wrong there?
You're missing :main-opts
:nrepl-opts {:main-opts ["-m" ...]}
yeah, that works, was trying to avoid the WARNING: Use of :main-opts with -A is deprecated. Use -M instead.
clojure -M:dev:nrepl:nrepl-opts
will avoid that warning.
Did that work @kaxaw75836?
yep, that work thanks, was not seeing the warning again when reverting to -A, but removing .cpcache put it back on
Ah, yes, -Sforce
is your friend there
There's definitely going to be a bit of adjustment in the CLI world as all the projects and tutorials get updated to the latest set of options. I've got to go through my open source projects and update the READMEs -- and update clj-new
so it generates all the up-to-date deps.edn
and project docs etc.
I can follow along now, took a while to figure -M -m in the cli
Yeah, there's quite a bit of discussion about the -M -m
"repetition" but it's the start of a grand plan that will bring more functionality over the next few releases...
The general guidance is that you use just one of -A
(for REPL dependencies), -M
(for running any main opts), or -X
(for executing functions) and just specify all the aliases you need via that one option.
The confusing part right now is that -A
still executes :main-opts
-- which it will stop doing at some point.
(which is why we still have -R
and -C
-- but the real solution is to split :main-opts
out of the aliases that you would otherwise want to use with -A
-- as you have done above -- so you can do clj -A:dev:nrepl
to start a REPL but clj -M:dev:nrepl:nrepl-opts
to run the nREPL command-line)
you mean clj -A:dev:nrepl
if you want to start a REPL programmatically for example correct? and clj -M:dev:nrepl:nrepl-opts
to start it from CLI?
:
not /
yeah, typo 😉
@kaxaw75836 I've simplified my aliases to just use -M (or -X where supported). Lots of examples here https://github.com/practicalli/clojure-deps-edn
hey, small correction: reveal supports -X flag
Say you have this (def foo [{:a "b" :e "f"} {:a "c" :g "h"}])
e.g.
{:extra-deps {vlaaad/reveal {:mvn/version "1.0.130"}}
:ns-default vlaaad.reveal
:exec-fn repl}
Is there a beter way to remove all a
's, than this?
(mapv #(dissoc % :a) foo)
[{:e "f"} {:g "h"}]
that seems to line up semantically with exactly what you are trying to do, ie, remove the :a
key from vector of maps. Is there something deficient about it? Wondering why you would ask
Perhaps there was some deep knowledge within the standard library that would do that 🙂, you know, one function and all 🙂
but I'm happy that my first attempt hit home 😉
Always looking to see if I can improve my understanding of the standard library, by learning about a function that I don't know of yet.
then you can use it as is:
$ clj -X:reveal
Clojure 1.10.1
user=> 1
1
override args:
$ clj -X:reveal :title '"hello"'
Clojure 1.10.1
user=> 1
1
use different type of repl:
$ clj -X:reveal io-prepl :title '"hello"'
1
{:tag :ret, :val "1", :ns "user", :ms 0, :form "1"}
it's all already there
I have an API that makes some decisions based upon the input. Depending on a value of one of the input, a requires-resolve
is invoked to load in a namespace. Are there any downsides (memory?) of that requires-resolve
being invoked again and again after the first time (i.e., once the namespace has been resolved)?
not really. some perf impact but I assume it's negligible unless you're calling it in a hot loop
great, thank you. no. it's called infrequently 🙂
Awesome. I've added the Clojure exec config to the existing aliases in the repository. So the aliases can be used with either -M
or -X
, with example of using -X
to call other REPLS.
(def colla [{:email "<mailto:A@b.com|A@b.com>" :ip "10.0.0.1"} {:email "<mailto:b@b.com|b@b.com>" :ip "10.0.0.2"}])
(def collb [{:email "<mailto:a@b.com|a@b.com>" :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>" :new_email "<mailto:d@b.com|d@b.com>"}])
How to merge 2 lists of maps where they may share the same field? I have 2 vectors of maps, they may share an :email key, I would like to merge the maps. The only solution I have come up with is really ugly, is there a better way?
(mapv (fn [i]
(let [email (string/lower-case (:email i))]
(merge i (first (filter #(= (string/lower-case (:email %)) email) collb))))) colla)
[{:email "<mailto:a@b.com|a@b.com>", :ip "10.0.0.1", :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>", :ip "10.0.0.2", :new_email "<mailto:d@b.com|d@b.com>"}]
clojure.set
solution
(let [colla [{:email "<mailto:A@b.com|A@b.com>" :ip "10.0.0.1"}
{:email "<mailto:b@b.com|b@b.com>" :ip "10.0.0.2"}]
collb [{:email "<mailto:a@b.com|a@b.com>" :new_email "<mailto:c@b.com|c@b.com>"}
{:email "<mailto:B@b.com|B@b.com>" :new_email "<mailto:d@b.com|d@b.com>"}]
xf-lower-email (map (fn [x]
(if (string? (:email x))
(update x :email string/lower-case)
x)))]
(clojure.set/join (into #{} xf-lower-email colla)
(into #{} xf-lower-email collb)
{:email :email}))
what do you want to happen in the event that the maps share the same key? i think with merge
, the last value in wins
there's also merge-with
which, i believe, lets you pass in an explicit merging strategy
merging lists is not a thing. You can concatenate lists, or merge maps
assuming you mean "merge maps repeatedly", @michael740’s question is relevant -- what is the desired behavior with two maps?
and if that's a bad assumption -- perhaps an example of what you want would clarify
I updated the question with the output from the working solution, It works fine, but it looks wrong to me, like I am missing a fundamental core function that would simplify the operation.
you can map
over n collections, too
If these two vectors can be very long, and you are concerned about efficiency of this operation, then creating a map from the second vector, where the key is the :email
value of the map, and the corresponding value is the map with the :email
from the second vector, would be a good first step, so you do not need to do a linear scan of the second vector each time.
But given the output you want and the sample code you gave, I don't think there is a lot shorter Clojure code one could write using the core library of functions to achieve what you want.
And if the linear scan of the second vector is not a performance issue for you, then I would say that I don't consider the code you wrote ugly, personally.
(map merge
[{:name "michael"} {:name "stokley"}]
[{:name "andy"} {:name "fingerhut"}])
;; => ({:name "andy"} {:name "fingerhut"})
(map + [1 2] [3 4])
;; => (4 6)
@michael819 :man-shrugging: ^ ?
Another thing to note about your code is that if there was a map in the second vector with an :email
that did not appear anywhere in the first vector, it would not be included in the result. Maybe you know that this is OK from the context of where you want to do this.
That's actually intended, the actual maps have different sizes, and some may have an email while others don't.
I just thought matching field values would be something the core would include but I didn't find anything in the docs and wanted to be sure I didn't miss anything. Still wrapping my head around functional programming. Thanks for the feedback.
The vectors aren't the same size, and the map shape is slightly different.
You could perhaps consider writing a separate named function like (defn my-email [user] (string/lower-case (:email user)))
, and then call that twice:
(mapv (fn [user]
(let [email (my-email user)]
(merge i (first (filter #(= (my-email %) email) collb))))) colla)
Yeah this was a one-off I didn't even write a function, just did in in the repl, but I would pull that out into a seperate function if I was going to prod with it.
user=> (group-by #(str/lower-case (:email %)) (concat colla collb))
{"<mailto:a@b.com|a@b.com>"
[{:email "<mailto:A@b.com|A@b.com>", :ip "10.0.0.1"}
{:email "<mailto:a@b.com|a@b.com>", :new_email "<mailto:c@b.com|c@b.com>"}],
"<mailto:b@b.com|b@b.com>"
[{:email "<mailto:b@b.com|b@b.com>", :ip "10.0.0.2"}
{:email "<mailto:B@b.com|B@b.com>", :new_email "<mailto:d@b.com|d@b.com>"}]}
user=> (map #(apply merge %) (vals *1))
({:email "<mailto:a@b.com|a@b.com>", :ip "10.0.0.1", :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>", :ip "10.0.0.2", :new_email "<mailto:d@b.com|d@b.com>"})
@michael819you can build up a lookup map by the normalized email
then merge the right-hand side (the values) of the map
still, better to normalize the emails at the outset
its a join
also, ^. See clojure.set
Doh! Of course. Still haven't had first coffee of the day.
a kind of generic hash-equi join algorithm for 2 collections is 1. index the smaller collection (this is the hash) 2. walk each item in the larger collection using the hash to find items in the smaller to join to
which is kind of similar to the group-by above
https://github.com/clojure/clojure/blob/master/src/clj/clojure/set.clj#L111-L140 is an example which designed to work with sets, which means you lose any ordering you may want to preserve
For interop, if a method has setData(Map<String, String> data)
, is it as simple as doing (.setData object {"a" "b" "c" "d"})
?
yes @dharrigan, because clojure maps implement java.util.Map
w00t thank you 🙂
yup, works flawlessly 🙂
fantastico
@ghadi That begs a question which often floats around in my mind: do you tend to use class
and supers
rather than type
and ancestors
(and only switch to the latter pair when you want something outside the "type" (class) hierarchy)?
I have used type/ancestors probably a couple times in 10 years
(and, related, does ClojureScript have class
/`supers`?)
I'm usually interested in class hierarchy
type
and ancestors
are built upon class
/ supers
, so not as primitive
ClojureScript does not have class
, only type
I just fired up a cljs REPL to check and I see it has ancestors
but of course the type
s of a lot of things in cljs do not have any ancestors... 👀