I think I found a difference in reduce
with next.jdbc/plan
and a regular collection. When using reduce
with a regular collection I can make decisions based on the previous element if I want. With next.jdbc/plan
it seems not possible. Since I’m a beginner I could be doing something wrong, of course.
The following code work as expected:
(defn append [entry]
(prn "Appending" entry))
(defn create-new [entry]
(prn "Creating new" entry))
(defn not-same? [a b]
(prn a "x" b)
(not= a b))
(reduce
(fn [prev-entry entry]
(if (not-same? entry prev-entry)
(create-new entry)
(append entry))
entry)
nil
[1 1 1 2 2 3 4 4 5])
The output is:
1 "x" nil
"Creating new" 1
1 "x" 1
"Appending" 1
1 "x" 1
"Appending" 1
2 "x" 1
"Creating new" 2
2 "x" 2
"Appending" 2
3 "x" 2
"Creating new" 3
4 "x" 3
"Creating new" 4
4 "x" 4
"Appending" 4
5 "x" 4
"Creating new" 5
The following (equivalent?) code with next.jdbc/plan
doesn’t:
(defn append [entry]
(prn "Appending" entry))
(defn create-new [entry]
(prn "Creating new" entry))
(defn not-same? [a b]
(prn a "x" b)
(not= a b))
(reduce
(fn [prev-entry entry]
(if (not-same? entry prev-entry)
(create-new entry)
(append entry))
entry)
nil
(jdbc/plan db ["SELECT 1 AS a UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 4 UNION ALL SELECT 5"]))
The output is:
{:a 1} "x" nil
"Creating new" {:a 1}
{:a 1} "x" {:a 1}
"Appending" {:a 1}
{:a 1} "x" {:a 1}
"Appending" {:a 1}
{:a 2} "x" {:a 2}
"Appending" {:a 2}
{:a 2} "x" {:a 2}
"Appending" {:a 2}
{:a 3} "x" {:a 3}
"Appending" {:a 3}
{:a 4} "x" {:a 4}
"Appending" {:a 4}
{:a 4} "x" {:a 4}
"Appending" {:a 4}
{:a 5} "x" {:a 5}
"Appending" {:a 5}
Seems like the IReduceInit
protocol implemented by next.jdbc
is always calling the reducer with the current element of the result-set.
@seancorfield Hi, I believe you are the maintainer. Should I open a bug report for this or I’m just doing it wrong? Thanks!next.jdbc/plan
provides a reducible abstraction over a mutable ResultSet
object from Java. The ResultSet
can only point at the current row during each invocation of the reducing function: the "previous" row is no longer available.
I know, the same applies with the first vector example, right?
However, you are also using plan
incorrectly by trying to use "whole rows": the intent of the abstraction is that you can get the value of specific columns without realizing the whole row. If you do want the whole row, you should call datafiable-row
on it to realize it into a Clojure data structure.
Hum
(and in fact if you realize each row using datafiable-row
then your reduce
will actually work because you're dealing with just Clojure data at that point and not the (mutable) ResultSet
object)
Oh, I understand now. That’s the issue then. ResultSet is mutable and I’m reducing the same object 😐
Right. plan
has a pretty specific use case -- and the reduction takes care of opening and closing a connection to the database (or connection pool).
Thank you!
If you have SQL-specific Qs, or questions about next.jdbc
, I'm more likely to see it in the #sql channel which is lower traffic than #beginners where I can easily miss something.
Oh good, didn’t know that. Will join, thank you once again.
Ah, I just made the code work with rs/datafiable-row
😄
I’m trying to (clojure.tools.namespace.repl/refresh)
, but it fails when it tries to process some cljs stuff. I’m in the jvm repl. I suspect some cljc file doesn’t play nice. Is there a good way to track down what causes this?
It is a clj & cljs project and I have started both repls in CIDER.
What would you say is the best resource to learn parallel programming in clojure? I'm interested in both the primitive constructs of the language and core.async
none of the clojure specific constructs for parallelism are "primitive", they build on the vm primitives to add some conveniences
much of the design of the language is set up so that parallel code works as much like non parallel code as possible (eg. the way atoms / refs / agents are designed are various ways to simplify mutable values in parallel code)
I wouldn't suggest ever using core.async to make your code parallel, it's a good tool for coordinating flows of values which may or may not happen in parallel as opposed to just async. using core.async to make code parallel rarely improves things in my experience, but once you have things that are already realized in undefined order it's good for simplifying that code
regarding your actual question I don't know of any good resources, sorry - though this talk expands on some stuff about core.async https://www.youtube.com/watch?v=096pIlA3GDo
rewatching, it's also a great lesson via example of refactoring ugly clojure code
I'm trying to get a sheet title using this API https://github.com/SparkFund/google-apps-clj/blob/develop/src/google_apps_clj/google_sheets_v4.clj
(defn get-sheet-titles
[service spreadsheet-id]
"returns a list of [sheet-title sheet-id] tuples. It seems the order reflects
the order of the tabs in google's interface, though I doubt this is anywhere
guaranteed."
(->> (get (get-spreadsheet-info service spreadsheet-id) "sheets")
(map #(get % "properties"))
(mapv (juxt #(get % "title") #(get % "sheetId")))))
this is the code i wrote
(ns asdf.core
(:require [clojure.edn :as edn]
[clojure.test :refer :all]
[google-apps-clj.google-sheets-v4 :refer :all :as gsheets]))
(defn login
[]
(gsheets/build-service "<http://1080463043693-gmysecretid.apps.googleusercontent.com|1080463043693-gmysecretid.apps.googleusercontent.com>"
"_la3mykeysecretJ"))
(login)
(gsheets/get-sheet-titles
"16qb3Zg4kdD62y7cf0oQ4TrRmq890V19fpnu7hmRpTeg")
but i got an error said :
Execution error (ArityException) at asdf.core/eval4230 (form-init16301128171610314250.clj:63).
Wrong number of args (1) passed to: google-apps-clj.google-sheets-v4/get-sheet-titles
where's the mistake?nvm, i found it, i must wrote this instead
(gsheets/get-sheet-titles
(login)
"16qb3Zg4kdD62y7cf0oQ4TrRmq890V19fpnu7hmRpTeg")
I have no idea if that is some kind of password or only some shorter-lived authentication string that no one else can take advantage of, but you might want to be careful about publishing such things to the world 🙂
Thanks for reminding, yes & no worries i've modified the authentication & delete the authentication after posting
@andy.fingerhut I'd like to ask if you can help me, how to write Sheet1!B9 using this guidance below?
"sheet-ranges is a seq of strings, using the A1 syntax, eg [\"Sheet!A1:Z9\"]
Returns a vector of tables in corresponding to sheet-ranges. Only one
sheet (tab) can be specified per batch, due to a quirk of Google's API as far
as we can tell."
So in Clojure source code, [ "a" "b" ] is a vector containing two strings.
What you show above looks like it was copied and pasted from a Clojure program source file?
yes
I guess that because the backslashes in front of each double-quote character (") are needed inside of a Clojure string if you want a double-quote to be a character in the string, i.e. you don't want that double-quote to end the string
(defn get-cell-values
"sheet-ranges is a seq of strings, using the A1 syntax, eg [\"Sheet!A1:Z9\"]
Returns a vector of tables in corresponding to sheet-ranges. Only one
sheet (tab) can be specified per batch, due to a quirk of Google's API as far
as we can tell."
[^Sheets service spreadsheet-id sheet-ranges]
(let [tables (get-cells service spreadsheet-id sheet-ranges)]
(mapv (partial mapv (partial mapv cell->clj)) tables)))
this is the complete,So if you look at the documentation as it would show up normally, it would be saying ["Sheet!A1:Z3"]
, which is a vector containing 1 string
The earlier English docs say "a seq of strings", where "seq" is short for "sequence". A Clojure vector is one kind of thing that will be treated as a sequence, as will a list and a few other Clojure data structures.
I've never used this API, by the way -- I'm just mentioning a few facts about Clojure that you might already know.
If you are trying to get only the value of 1 cell in the sheet, I would attempt to use something like ["Sheet!B9"]
, a vector of 1 string, and see if it works.
Ahhh yes it works, we need to define the exact name of the sheet though... thanks a lot!
(gsheets/get-cell-values
(login)
"16qb3Zg4kthisworksq890V19fpnu7hmRpTeg"
["Sheet1!B9"])
cool
If I want a small, local and easy to use database. Is sqlite or h2 the way to go or is there anything else that fits well with clojure?
I am partial to apache derby
Which was even bundled with some jdks at one point as "javadb"
Using sqlite from the jvm usually ends up either needing to load a native library, or using native code compiled to arm asm, and then the arm asm turned into jvm bytecode
So tricky and not always portable
Ok, cool! I will check out derby
The few times I have tried h2 I have run afoul of concurrency issues (this might have been a configuration issue)
hsqldb (aka HyperSQL) is also an option. Works well with native-image too