(remove empty? (for [issue ((run-jql-query
"project = \"Connect\" and issueLinkType in (\"relates to\") and resolution is empty"
:expand "issueLinks"
:fields "issuelinks"
:max-results 10)
:issues)]
(let [links (filter #(not= % (first (string/split (issue :key) #"-")))
(dedupe
(for [outboundLink (remove nil? (for [link (-> issue :fields :issuelinks)]
(get-in link [:outwardIssue :key])))]
(first (string/split outboundLink #"-")))))]
(if (not-empty links)
(hash-map (keyword (first (string/split (issue :key) #"-")))
(to-array (for [link links]
(keyword link))))))))
I’m a little ashamed of how messy that probably is. But such is life as a beginner.
It starts as a query to the JIRA API, grabbing some pieces of the response, and trying to build a map of projects to the other projects they link out to. Goal is to build a graphviz graph from that.what does the rest of the stacktrace say?
oh
actually
you are making primitve java arrays with to-array
(the [Ljava.lang.Object; in the error message)
and that is the error message you get when you try to use clojure.set/union with primitive arrays
stuff in clojure.set is generally intended to work on clojure's native set datatype, but it doesn't really do any input type checking, so sometimes people use it with collections that are not sets, and that sometimes works
but primitive java arrays are not collections or sets
aahh
That fixed it.
Than kyou so much @hiredman and @robert.mitchell36
@borkdude Sorry, been head down in a code spike all afternoon (after getting my brain swabbed for COVID earlier today which was unpleasant...)... So, er, what does Leiningen actually do here?
Does lein
just binary-copy those files into the uberjar?
You don't want to rely on a dependency manager because it can't handle reproducibility and rollbacks as well
So like if you just installed clj
on the server, than ran your code, its possible the dependencies it pulls down will be different on one node from another
Its possible to do it though, if you use something like Artifactory
Where you can make sure your own repo of artifacts is immutable, and the version you depend on will always be exactly the same thing
Then with git deps using shas, that covers your own code base.
But short of being sure that your deps.edn deps are guaranteed to be the same every time you pull them down, for the same version you specified, it could get you in trouble.
That and, if you depend on public repos too, like Clojars, that can go down at any time, someone can pull out their lib from it, or someone could take it over and replace some of the lib their with compromised ones
That said... I never thought about this, but maybe if you packaged the clj local deps and classpath cache, and replicated that across your servers, but that is actually exactly what an UberJar is, just a lot more convenient then this
Like the act of creating an UberJar is just taking all your dependencies and your code and putting it in a Zip file.
Just one you don't have to unzip, and can just launch your app straight from the zip
transducers clicked for me after reading rich's history of clojure paper https://dl.acm.org/doi/pdf/10.1145/3386321
there's some good info in there
good morning all
Why does this fail on 4clojure
#(and (contains? %1 %2) (nil? (get %1 %2)))
tests :
Write a function which, given a key and map, returns true iff the map contains an entry with that key and its value is nil.
(true? (__ :a {:a nil :b 2}))
(false? (__ :b {:a nil :b 2}))
(false? (__ :c {:a nil :b 2}))
what error do you get? I remember trying to do something similar and it was not possible to use and like that because it's a macro
maybe the (fn ..) syntax will work
no, error on the 4clojure site only a message that I fail the tests
yep, this is working
(fn test [ key map]
(and (contains? map key) (nil? (get map key))))
yeah actually i think i was wrong
you just mixed the order of the arguments
in you first definiton
this works
(true? (#(and (contains? %2 %1) (nil? (get %2 %1))) :a {:a nil :b 2}))
oops
is this a good clojure solution
When retrieving values from a map, you can specify default values in case the key is not found:
(fn test[default sequence]
(into {} (for [x sequence] {x default})))
You can use find too
i also found that
#(nil? (get %2 %1 "nope"))
works but it's not really obvious what one should use in the default case
yep, i agree. the challenge is not clear about it
I think the challenge is clear, and using a default value for the get i think makes sense, you just have to put something that is not nil as the default, but i'm not sure what would be the best option
oke
How about : #(nil? (get %2 %1 :else))
Something basic to see if I understand correctly. If I want to use a non-standard library: • how to I see it's not standard? doesn't start with "clojure/"? • Do I always have to state my intent in two places? With leinigen in the project.clj, so Leinigen can download it and make it available and in the namespace again so I can use it in the namespace?
I think "standard library" is the content of clojure.core namespace. Everything else is a non-standard. And yes to your second question in case of leiningen
As for the second point, yes, but it’s helpful to note that there are two intentions here: expressing what you intend to use (a concrete version of a library), and where you intend to use it (in this or that ns). You express the first intention to your deps management tool (project.clj for lein, deps.edn for clj, etc), and express the second in the namespace itself.
Why do we need to use partial
function? We can achieve it using normal defn
right? why we go for partial
?
There is never a time where partial
can be used, that one cannot use (fn ...)
or sometimes #( ...)
instead. So in that sense there is no need to use partial
. Some people prefer partial
when it does the job.
nope, it fails the first case
(= (__ 0 [:a :b :c]) {:a 0 :b 0 :c 0})
I have solved this challenge : https://www.4clojure.com/problem/156
(fn test[default sequence]
(into {} (for [x sequence] {x default})))
is that good clojure code or can I improve it somehow ?
Looks good to me. An alternative would be
(fn [default ks]
(zipmap ks (repeat default)))
Maybe be careful with the names - sequence and test are both already core functions.Good warnings on names. Although, the shorter the function, the less likely such shadowing of names is to cause problems, and that is a nice short function.
thanks both
so ks
is a better name for a collection
or maybe col
?
Most commonly you will see xs
(as in "a collection of x's") to refer to a generic collection of things.
oke, i know that one from my haskell days
there is xs
also often used
anyone xp with clj.http to make this work instead of slurp `(slurp "https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True") ?
(client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
vrey quick answer
I can use the same trick here
(str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles?key=14OGzuak&format=json")
where the objectnumber is a variable from a loop
yes
oke, how do I handle the objectnumber as variable then ?
(some #(contains? % "xx") #{"xx" "yy" "jj"})
this is giving me an issue
(for [on ons] (use on))
but this is returning result (contains? #{"xx" "yy" "jj"} "xx")
is there any issue with the 1st function?
that not what I mean
Have you seen the examples in the docs? https://github.com/dakrone/clj-http#get
(client/get
(str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles")
{:query-params ...})
so something like this :
(client/get "<https://www.rijksmuseum.nl/api/nl/??/tiles>"
{:query-params {:key "14OGzuak"
:format "json"
:object-number :object-number"}})
1st fn, 1st contains call: (contains? "xx" "xx")
=> contains? not supported on type: java.lang.String
I think it will clarify if you unpack the expression. What is #(contains? % "xx")
being applied to in the first case?
i am passing set in place of %
Is object-number
part of the query string?
yep, is it a variable part
Seen my answer there yes?
Noting: query-params it's what's after the "?" bit.
so I can just use it as a variable
the url path you concatenate, yes
Understandable you would think that, but it is not the case here. Check out the docs for https://clojuredocs.org/clojure.core/some
https://github.com/metosin/reitit can do "reverse routing": template + path params = http path
I am trying to do same as
(some #(= 5 %) [6 7 8 9 10])
which is mentioned in document
(def router (r/router ["/api"
["/foo" ::foo]
["/bar/:id" ::bar]]))
(:path (r/match-by-name router ::foo)) ;; /api/foo
(:path (r/match-by-name router ::bar {:id 1})) ;; /api/bar/1
oke, I did not learn reitit
or, is it just taking first element from set?
Just compujure and ring so far
(some pred coll)
Returns the first logical true value of (pred x) for any x in coll,
else nil.
So you are applying #(contains? % "xx")
to every element of the set #{"xx" "yy" "jj"}
Not the set itself.
In other words, (contains? "xx" "xx")
as clyfe mentions.yeah...
(some #(= % "xx") #{"xx" "yy" "jj"}) worked
can we use = in comparing string in clojure?
does it check string content ? or reference?
maybe https://clojuredocs.org/clojure.string/includes_q is what you want?
include does not suit my requitement
want to check exact string
= then
> can we use = in comparing string in clojure? I think in general, yeah you're pretty safe doing that
does it check the string content?
yes
(defn =
"Equality. Returns true if x equals y, false if not. Same as
Java x.equals(y) except it also works for nil, and compares
numbers and collections in a type-independent manner. Clojure's immutable data
structures define equals() (and thus =) as a value, not an identity,
comparison."
yeah this is i got from core code
thanks
https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#equals(java.lang.Object)
Ppl, what is the purpose of using a new argument with reduce in this example? What he does? Thank you!
@claudioferreira.dev If you don't provide the "initial" value in reduce
, it has some slightly strange semantics. Take a close look at the docstring.
"If val is not supplied, returns the result of applying f to the first 2 items in coll, then applying f to that result and the 3rd item, etc." -- That's fine. But pay attention to this bit: "If coll contains no items, f must accept no arguments as well, and reduce returns the result of calling f with no arguments."
And you you have this case (again, when "initial" value is not provided): "If coll has only 1 item, it is returned and f is not called."
For example:
user=> (reduce println [1])
1 ; println not called, returns first (only) value of the collection
user=> (reduce println [] [1])
[] 1 ; println called -- prints initial value and first element
nil ; returns nil because that's what println produces
Hi I can totally replace ClojureScript inside an existing React client project?
And then with no elements in the collection:
user=> (reduce println [])
; println is called here with no arguments
nil ; returned from calling println
user=> (reduce println [] [])
[] ; doesn't call println, returns initial value
The latter is easier to see with (reduce println [1] [])
which just returns [1]
Does that help @claudioferreira.dev?
Yeah, thats make sense about the Ireduce problem.
Thank you @seancorfield!!!! Now i understand this example. I appreciate your patience and attention
How does ClojureScript handle state management inside an existing React project then?
@sebastian_cheung Reagent and Om are ClojureScript libraries that wrap React.js under the hood. re-frame is built on top of Reagent to provide a structured way to manage state changes. You should look at the docs for those projects.
Essentially you would replace all your JS (and React.js usage) with a complete, new ClojureScript app that used re-frame, or Reagent alone, or Om etc
thanks @seancorfield but I will be working in a team, they will not change their React code just for me, so only potentially ClojureScript from my side, interfacing with their React features
@sebastian_cheung My understanding is that is not possible. cljs assumes "whole program".
There are several clojurescript projects that work well with React (including react and re-frame). I wouldn't suggest Om for new projects though. From Om's Readme: > NOTE: This project is no longer under active development. If you'd like to use a library that's well maintained that was inspired by some of the ideas presented here see https://github.com/fulcrologic/fulcro
Glad to hear that Om is directing users to Fulcro. Does the same apply to Om.Next?
Yes, afaik
I believe using clojurescript to interop with existing react code is possible. I think https://github.com/lilactown/helix might be a good library to start with for that use case.
per its https://github.com/lilactown/helix/blob/master/docs/motivation.md: > The goals of Helix are: > - Provide an ergonomic, well-documented API for building components in ClojureScript > - Have as small of a runtime as possible > - Add as few new semantics on top of React as possible
Fulcro or helix then?
Hmm, that repo is where I end up if I follow links for Om Next so I guess both versions of Om have gone away. Can't say I liked it, compared to Reagent -- at least when we looked at apps built with both Om and Reagent back in 2013/2014 🙂
@smith.adriane Reading the docs for Helix, it's not clear to me that you could use it to define React components/hooks and use it as part of an existing JS React.js app?
I've been trying to escape the tyranny of the browser. I was able to get Fulcro to work on desktop. re-frame has implicit dependencies on react. I think that may be the case with reagent as well.
@sebastian_cheung I think Fulcro is also going to assume that it's in charge of the whole app too, like re-frame/Reagent...?
Couldn’t the CLJS be compiled to an npm module & required by the JS? I’ve never done it, but these docs look promising: https://shadow-cljs.github.io/docs/UsersGuide.html#target-npm-module
maybe @lilactown could provide better clarification. from this https://github.com/lilactown/helix/blob/master/docs/creating-components.md#interop : > One thing to note is that this conversion of JS objects to CLJS data types is shallow; this means if you pass in data like a JS object, array, etc. to a prop, it will be left alone. > > This is an intentional design decision. It is a tradeoff - on the one hand, it is more efficient to opt not to deeply convert JS data structures to CLJS data, and it means that you do not need to learn some Helix-specific rules when interoping with external React libraries that use higher-order components or render props.
Oh, that does look like it might help @sebastian_cheung build stuff with cljs and integrate it into an existing React JS app?
see that web development is now a lot about react and sons
also: > Helix's philosophy is to give you a Clojure-friendly API to raw React. All Helix components are React components, and vice-versa; any external React library can be used with Helix with as minimal interop ceremony as possible.
re-frame can be used server-side in Clojure -- it's kind of weird but it's possible...
I meant for building desktop apps.
With... Electron or something similar?
my side project is trying 🤞 to clojurize UI development: https://github.com/phronmophobic/membrane
most of the cool UI development is happening in cljs with react in mind
but where possible, I've tried to use existing UI state libraries without react
so mayb in the futuure look for a coure how to make a html site into a react site
I'm going through a lot of material and courses right now about getting started with re-frame -- SPAs that make API calls to Clojure on the backend.
is recurring implicitly from tail position the same as using recur
or must you explictly use recur
?
@octo221 You have to use recur
explicitly. Otherwise it is going to use the stack for recursion and you may blow the stack.
recur
is explicit so that you can only use it in a position where it can avoid using the stack.
(if you try to use it in a non-tail position, you'll get a compiler error)
thank you @seancorfield
but if the compiler can give error when`recur`is used in non-tail position, why can't it replace implicit recur with recur
automatically ?
(when called from tail position)
That's a design decision that Rich has talked about. He wanted folks to be able to look at code and immediately tell whether it was doing actual recursion or the stack-friendly "tail call optimization", i.e., recur
.
I see
yes I see that makes good sense
When we see recur
we know it's "safe" and using the optimized form: looping with new bindings rather than actually doing recursive calls.
When we see a recursive function call, we know it's using the stack. So it makes it simple to see what's going on -- and not have to try to discern what the compiler will do with our code behind the scenes.
good explanation thanks
@seancorfield do you have then a good course for me to learn re-frame ?
oke, I think i try the free course first. Find 200 till 1000 euro very much for a hobby
I also think having implicit recur would give you the false impression that the compiler can detect mutually recursive tail calls, which it cannot. By forcing the use of recur you know it's not possible for you to recurse to another function that will call you back.
how can I convert keys here to keywords :
`->>(client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
(:body)
(json/parse-string)))
@smith.adriane @seancorfield @sebastian_cheung it's 💯 possible to use components defined using helix in a ReactJS app. care has to be taken to accept props in a way that is ergonomic for the consumer, though. E.g. you probably won't want to expect that someone using your components are passing in ClojureScript maps, vectors, sets etc.
it's also possible to do this with Fulcro, it requires more steps and care though
personally, I would not try and develop a bunch of components in CLJS that are then used in a React app, as the tradeoffs are not worth it. ClojureScript is a good application language. You'll find that when creating libraries that are then used in JS, those libraries are harder to build, to use, and will have worse performance than a JS lib
I have had great success going the other direction, building components in JS that I then use in CLJS. Helix had that in mind when it was built, and allows you to use ReactJS components basically the same as if you defined them using Helix
Just pass the right options to json/parse-string
This is data.json
right? It's right there in the README @roelof: https://github.com/clojure/data.json#converting-keyvalue-types
Thanks for the follow-up @lilactown -- that sort of confirms the impression that I'd gotten about cljs vs JS (use cljs for the app, JS for interop/components as needed) but interesting to hear that you can write components in cljs and use them from JS/React.js but it has caveats...
nope, this is cheshire
Then read Cheshire's docs -- that readme also covers this.
think I gave up on clojure
(json/parse-string (client/get
(str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles")
{:query-params {:key "14OGzuak"
:format "json"}}))))
error:
; Execution error (ClassCastException) at cheshire.core/parse-string (core.clj:207).
; class clojure.lang.PersistentHashMap cannot be cast to class java.lang.String (clojure.lang.PersistentHashMap is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
@roelof look at what client/get
returns -- try it in the REPL.
It is not a string. It's a hash map.
chips, I see it
parse-string
expects a string and you're handing it a hash map.
never code just before goto sleep
You need to learn to take more careful steps and try things out in the REPL.
oke
do you like this code :
(ns paintings.core
(:require [cheshire.core :as json]
[clj-http.client :as client]
[clojure.walk :as walk]))
(defn image-url-size [image]
(let [data (select-keys image [:width :height])
url (get-in image [:tiles 0 :url])]
(assoc data :url url)))
(defn assoc-image [object-number]
(->> (client/get (str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles")
{:query-params {:key "14OGzuak"
:format "json"}})
(:body)
(json/parse-string)
(walk/keywordize-keys)
(:levels)
(sort-by :name)
(last)
(image-url-size)
(merge {:object-number object-number})))
(time (->>(client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
(:body)
(json/parse-string)
(walk/keywordize-keys)
(:artObjects)
(map :objectNumber)
(map assoc-image)))
Like I just said, you don't need keywordize-keys
. Just tell Cheshire to return keywords.
yep but then I think I cannot use ->>
but then I have to use ->
Be careful about using time
around a map
expression because map
is lazy.
You can use ->
for most of the pipeline and then switch to ->>
for the final steps.
can I ? never seen that
But it's considered poor style to mix threading types -- because it indicates you are changing from "object/thing/collection" functions to sequence functions.
Can I what?
I learned I have to use a function to use ->
in a ->>
pipeline
(-> (client/get ..) :body (json/parse-string true) :artObjects (->> (map :objectNumber) (map assoc-image)))
okek but that is a poort style
so better not use it
Poor style because you're trying to do too much in a single pipeline. Refactor it into small functions to transform the data.
And don't forget about my comment about time
and map
.
I do
I will remember that the outcome is not the real time
time
will just tell you how long it took to construct the initial lazy seq, not how long it actually takes to do the transformation work.
oke
then I will delete it
Compare (time (mapv inc (range 1000)))
and (time (map inc (range 1000)))
I was hoping it would say how long it all took
(time (doall (map ..)))
if you must use map
. Or use an eager process like mapv
.
the map is much faster then the mapv one
Kinda! @seancorfield I understood that relation between f, val and coll. But i cannot understand how can that be useful in our day by day. Which problem does adding " { } " (just like in the pic i sent initially) as a initial value solves or facilitate our life? e.g
(reduce + [1 2 3])
=> 6 ; Great, we added all the values together
(reduce + {} [1 2 3]) ; ??? "{ }" helps nothing here
Execution error (ClassCastException)...
In this? "Compare (time (mapv inc (range 1000))) and (time (map inc (range 1000)))" -- no, that's exactly the point I was making: time
of map
doesn't time the work.
oke
I see a big difference
that is all I mean
Right, because (time (map inc (range 1000)))
is not timing how long it takes to map inc over that range.
It's just timing how long it takes to create an (unrealized) lazy sequence object -- it isn't doing any work.
im now trying to figure out why this is not working
(->> (client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
:body
(json/parse-string true)
:artObjects
(->> (map :objectNumber)
(map assoc-image)))
(time (doall (map inc (range 1000))))
will force realization of the mapping.
oke
You're using ->>
at the beginning, not ->
If so, why did the exercise added { } inside that maximum and minimum function? Just for the result be in a map? The book used "accumulator" to define that { }. So, we are taking our values from the coll [5 23 5004 845 22] and putting it inside the "acumulator"? (or "{}"/initial value)
It should be (-> (client/get ..) ... (->> (map ..) (map ..)))
time for me to sleep
but the code is working
Now you've fixed it 🙂 Do you understand what you did wrong?
Sorry for the dumb question @seancorfield , have been thinking for hours about this initial value but still didn't found the answer. Thanks for the support!
user=> (reduce + [1 2 3])
6
user=> (reduce + 0 [1 2 3])
6
user=> (reduce + 13 [1 2 3])
19
Hmmmm, just like partial?
yep
again I want to fast
very bad old habit
but this is better style
(defn take-data[image-data]
(->> image-data
(map :objectNumber)
(map assoc-image)))
(-> (client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
:body
(json/parse-string true)
:artObjects
(take-data))
Better, yes.
oke, tomorrow I try to rewrite this part
(defn assoc-image [object-number]
(->> (client/get (str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles")
{:query-params {:key "14OGzuak"
:format "json"}})
(:body)
(json/parse-string true)
(:levels)
(sort-by :name)
(last)
(image-url-size)
(merge {:object-number object-number})))
the same way
No.
Compare these:
user=> (reduce conj [] [1 2 3 4])
[1 2 3 4]
user=> (reduce conj {} [1 2 3 4])
Execution error (IllegalArgumentException) at user/eval173 (REPL:1).
Don't know how to create ISeq from: java.lang.Long
user=> (reduce conj #{} [1 2 3 4])
#{1 4 3 2}
user=> (reduce conj [] {:a 1, :b 2, :c 3})
[[:a 1] [:b 2] [:c 3]]
user=> (reduce conj {} {:a 1, :b 2, :c 3})
{:a 1, :b 2, :c 3}
user=> (reduce conj #{} {:a 1, :b 2, :c 3})
#{[:c 3] [:b 2] [:a 1]}
That's wrong.
???
I know that it's wrong
You're using ->>
again and you need ->
, and then switch to ->>
later in the pipeline
that what I rewrite it as you learn me afew moments ago
Oh, you mean you need to rewrite it "the same way" as you rewrote the other code?
Sorry, it's hard to understand what you're asking at times.
that is what I try to say that I will try to make that work tommorow
sorry, english is not my mother languages and I was years and years ago very bad in languages
at school
But, why does the values here go "back" to the initial value if we are not using conj?
(reduce (fn [{:keys [minimum maximum]} new-number]
{:minimum (if (and minimum (> new-number minimum))
minimum
new-number)
:maximum (if (and maximum (< new-number maximum))
maximum
new-number)})
{} ;; <---- The new argument!
[5 23 5004 845 22])
{:minimum 5, :maximum 5004}
And here they dont:
(reduce + { } [1 2 3])
Execution error (ClassCastException)
=> {6} ; The result i expectedNP. I get it now. Good luck tomorrow -- once you've had some sleep!
And remember: take small steps and try each piece out in the REPL.
In (reduce + {} [1 2 3])
the first step is (+ {} 1)
which is not legal.
In (reduce + 13 [1 2 3])
the first step is (+ 13 1)
=> 14
. The next step is (+ 14 2)
=> 16
. The final step is (+ 16 3)
=> 19
.
In your min max example, the first step is to call that function with {}
and 5
-- and the function destructures {}
against {:keys [minimum maximum]}
so both minimum
and maximum
are bound to nil
.
And the if
conditions check for nil
: (and minimum (> new-number minimum))
will guard the comparison for non-`nil` values.
So it produces {:minimum 5, :maximum 5}
The next step is to call that function with {:minimum 5, :maximum 5}
and 23
So now we get {:minimum 5, :maximum 23}
and go round again with 5004
and so on.
Does that help?
(reduce + [1 2 3])
is the same as (reduce + 1 [2 3])
so it does (+ 1 2)
and then (+ 3 3)
But (reduce + [])
is going to call (+)
-- with no arguments -- which produces 0
And (reduce + [1])
is just going to return 1
without calling +
.
You can see that +
is not called, in this example (reduce + [:not-a-number])
which produces :not-a-number
(if it tried to call +
on it, you'd get an exception).
yep, could not let it wait till tomorrow
what do you think
(ns paintings.core
(:require [cheshire.core :as json]
[clj-http.client :as client]
[clojure.walk :as walk]))
(defn image-url-size [image]
(let [data (select-keys image [:width :height])
url (get-in image [:tiles 0 :url])]
(assoc data :url url)))
(defn take-image-data[image-data object-number]
(->> image-data
(sort-by :name)
(last)
(image-url-size)
(merge {:object-number object-number})))
(defn assoc-image [object-number]
(-> (client/get (str "<https://www.rijksmuseum.nl/api/nl/collection/>" object-number "/tiles")
{:query-params {:key "14OGzuak"
:format "json"}})
(:body)
(json/parse-string true)
(:levels)
(take-image-data object-number)))
(defn take-data [api-data]
(->> api-data
(map :objectNumber)
(map assoc-image)))
(-> (client/get "<https://www.rijksmuseum.nl/api/nl/collection>"
{:query-params {:key "14OGzuak"
:format "json"
:type "schilderij"
:toppieces "True"}})
:body
(json/parse-string true)
:artObjects
(take-data))
GN all
Nice breakdown! Yay!
THANK YOU @seancorfield!!!! Finaaaally ive understand that!
oke, then now study ring and computure to make the back-end
and then decide what to use for the front-end
Cool. This higher-order stuff can be a bit bewildering at first because it's not how folks program in traditional languages.
In Clojure, using sequence and collection functions is far more common than writing loops, for example.
Rich Hickey has said several times that the IReduce
form of reduce
(without the initial value) was a bad idea and he wishes he'd only created the IReduceInit
form -- with three arguments: the reducing function, the initial value, and the sequence.
When I added "reducible queries" to clojure.java.jdbc
, I implemented both forms and it was a mistake. When I implemented next.jdbc
, I very deliberately omitted the IReduce
form.