Fork me on GitHub
#beginners
<
2021-04-04
>
Scott Starkey00:04:24

Hi there, folks. I am wanting to do a sort-by (or some sort of a sort) on a map with two keys in two directions. For example, I want to sort last name reverse-alphabetically, and then ties broken by first name by regular alphabetical order. So, given a list of data:

(def heroes '({:last-name "Stark", :first-name "Howard"}
              {:last-name "Wilson", :first-name "Sam"}
              {:last-name "Banner", :first-name "Bruce"})
              {:last-name "Stark", :first-name "Tony"})
I want it to sort like this:
({:last-name "Wilson", :first-name "Sam"}
 {:last-name "Stark", :first-name "Howard"}
 {:last-name "Stark", :first-name "Tony"}
 {:last-name "Banner", :first-name "Bruce"})
My brain is really swimming how to make this work. Can you help? 😱

phronmophobic00:04:29

there's probably a more succinct version, but this seems to work:

(sort-by (juxt :last-name :first-name)
         (fn [[x0 x1] [y0 y1]]
           (let [c (- (compare x0 y0))]
             (if-not (zero? c)
               c
               (compare x1 y1))))
         heroes)

Scott Starkey00:04:38

Ahhh, I was thinking about needing a compare function in there, but I could not figure out how to make it work. Thanks, @smith.adriane!

👍 3
dpsutton00:04:40

you don't need that custom compare i don't think. vectors sort positionally like that by default:

(sort-by (juxt :last-name :first-name)
         compare
         [{:last-name "Stark", :first-name "Howard"}
          {:last-name "Wilson", :first-name "Sam"}
          {:last-name "Banner", :first-name "Bruce"}
          {:last-name "Stark", :first-name "Tony"}])

phronmophobic00:04:45

I think the custom comparator is required to sort last name descending and first name ascending

phronmophobic00:04:33

using compare rather than the custom comparator gives a different order than the example output

dpsutton00:04:37

Oh sorry I missed descending. You’re totally right

andy.fingerhut01:04:32

This article might be way more than you want to know, but a good understanding of its contents might be what you are looking for: https://clojure.org/guides/comparators

👍 9
👆 3
💯 3
Dave A03:04:01

The only thing I could find that'd make @smith.adriane's solution more succinct is that you can reverse the order of the arguments of the first compare instead of negating the result:

(sort-by (juxt :last-name :first-name)
         (fn [[x0 x1] [y0 y1]]
           (let [c (compare y0 x0)]
             (if-not (zero? c)
               c
               (compare x1 y1))))
         heroes)
saves 4 whole characters! 🎉

andy.fingerhut04:04:55

I haven't checked carefully, but there are cases where using negation can lead to very subtle and occasional bugs in comparison functions, with some examples at the article I linked. That is a much more important reason to be wary of negation there than how many characters it is.

Michael Lan04:04:20

I am trying to make a Datomic account but it asks for an Organization when I am not part of one. Can I put “None” or something?

Michael Lan04:04:29

Also: are there still free editions of Datomic available for me to test out? There were some blogposts from ~8 years ago but the only plans seem to be Solo and Production

Michael Lan04:04:16

Also, what would I put for my organization?

seancorfield05:04:53

@U01ML6ZL2U8 "Free" doesn't exist any more -- that link hasn't had releases since 2019. You want the Cognitect Dev Tools to do free local dev with Datomic now. https://www.cognitect.com/dev-tools/ to download, https://docs.datomic.com/cloud/dev-local.html for more details.

seancorfield05:04:08

Datomic is all cloud now.

Michael Lan05:04:03

Thank you for letting me know!

Michael Lan05:04:12

wait, but the release date for the most recent version says 2021-03-09 though…

seancorfield05:04:19

The link @U0NCTKEV8 gave only has updates to 2019. The dev-local stuff has been updated recently, as you saw.

Michael Lan06:04:19

I used the Datomic Pro Free Edition thing. Is that outdated as well?

seancorfield06:04:43

If you are just learning Datomic, use the dev-local setup. If you are planning to run Datomic in production, you need a license even for the starter edition.

👍 3
seancorfield06:04:42

You can always ask in #datomic if you're not sure how to proceed.

sova-soars-the-sora14:04:29

how can i turn a string into a map in clojurescript? i thought it was cljs.reader/read-string but i'm not able to do normal map operations on the resultant piece even though it seems like one ought be able

andy.fingerhut14:04:19

What is the value of (type x), where x is the return value from (cljs.reader/read-string some-string)

sova-soars-the-sora15:04:37

you're a genius! let's see...

sova-soars-the-sora15:04:11

clojurescript say function PersistentArrayMap(meta, cnt, arr, __hash)

sova-soars-the-sora15:04:37

seems correct? o.O

sova-soars-the-sora15:04:43

thanks 😃 resolved

andy.fingerhut18:04:53

glad you found out the issue. I was AFK for a while there.

Phat Ky15:04:37

Hi. Please help me publish my test app on clojar.

Phat Ky15:04:07

I created a test app with this cmd lein new app my-app

Phat Ky15:04:04

I modified the project.clj file with this ..

1   │ (defproject my-app "0.1.0-SNAPSHOT"
   2   │   :description "testing deploy"
   3   │   :url ""
   4   │   :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
   5   │             :url ""}
   6   │   :dependencies [[org.clojure/clojure "1.10.1"]]
   7   │   :main my-app.core
   8   │   :target-path "target/%s"
   9   │   :profiles {:uberjar {:aot :all
  10   │                        :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Phat Ky15:04:37

Finally, I published with this cmd lein deploy clojars

Phat Ky15:04:02

My version of lein is Leiningen 2.9.5 on Java 11.0.10 OpenJDK 64-Bit Server VM

Phat Ky15:04:12

What am I doing wrong?

Phat Ky15:04:28

Here is the error message ...

No credentials found for clojars
See `lein help deploying` for how to configure credentials to avoid prompts.
Username: kyp0717
Password: 
Compiling my-app.core
Created /home/phage/tmp/my-app/target/my-app-0.1.0-SNAPSHOT.jar
Wrote /home/phage/tmp/my-app/pom.xml
Could not find metadata my-app:my-app:0.1.0-SNAPSHOT/maven-metadata.xml in clojars ()
Sending my-app/my-app/0.1.0-SNAPSHOT/my-app-0.1.0-20210404.155005-1.jar (14k)
    to 
Could not transfer artifact my-app:my-app:jar:0.1.0-20210404.155005-1 from/to clojars (): Failed to transfer file  with status code 401
Sending my-app/my-app/0.1.0-SNAPSHOT/my-app-0.1.0-20210404.155005-1.pom (2k)
    to 
Could not transfer artifact my-app:my-app:pom:0.1.0-20210404.155005-1 from/to clojars (): Failed to transfer file  with status code 401
Failed to deploy artifacts: Could not transfer artifact my-app:my-app:jar:0.1.0-20210404.155005-1 from/to clojars (): Failed to transfer file  with status code 401

sova-soars-the-sora15:04:28

@kyp0717 what's the error you get?

sova-soars-the-sora15:04:20

Unauthorized client error status

The HTTP 401 Unauthorized client error status response code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

Phat Ky15:04:38

@sova Do I need to set up a token? I assume NOT since it is just a simple test deployment to my group which is https://clojars.org/groups/org.clojars.kyp0717

sova-soars-the-sora15:04:07

This is a great question. I don't know anything about deploying to clojars 😄 but someone probably does.

sova-soars-the-sora15:04:14

Yes I think you need a token.

Phat Ky15:04:14

@sova I have tested my connection to clojar by successfully logging in to the group webpage. I am using the same id and password on the command line.

sova-soars-the-sora15:04:19

@kyp0717 hopefully the deploy token works.

sova-soars-the-sora15:04:53

To deploy to Clojars, you will need to authenticate yourself. Deployment authentication is done via your username and a Deploy Token.

Phat Ky15:04:01

I am trying to avoid using token as it said that token it is NOT needed for this type of deployment. Is this true?

Dave A15:04:28

https://github.com/clojars/clojars-web/wiki/Tutorial at the bottom note the group needs to be added to the defproject

Dave A15:04:19

so yours should probably be (defproject org.clojars.kyp0717/my-app "0.1.0-SNAPSHOT" ...

Dimitar Uzunov16:04:50

hi, is there a more idiomatic way to simply replace a value in map, disregarding the old val than this one:

(update map :something (fn [_ a] (function a)) 10)

schmee16:04:44

are you looking for assoc?

🙏 3
Dimitar Uzunov16:04:11

you are right! I am, I didn’t realise I can use it on existing keys, I thought it was just about adding new ones

sova-soars-the-sora16:04:19

assoc, dissoc, assoc-in, reset! are all some helpful friends

Phat Ky16:04:09

@vanchuck I modifed the project.clj file to ....

(defproject org.clojars.kyp0717/my-app "0.1.0-SNAPSHOT" 
  :description "testsing deploy"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :main my-app.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Phat Ky16:04:25

But I got this error ...

No credentials found for clojars
See `lein help deploying` for how to configure credentials to avoid prompts.
Username: kyp0717
Password: 
Compiling my-app.core
Created /home/phage/tmp/my-app/target/my-app-0.1.0-SNAPSHOT.jar
Wrote /home/phage/tmp/my-app/pom.xml
Could not find metadata org.clojars.kyp0717:my-app:0.1.0-SNAPSHOT/maven-metadata.xml in clojars ()
Sending org/clojars/kyp0717/my-app/0.1.0-SNAPSHOT/my-app-0.1.0-20210404.161209-1.jar (14k)
    to 
Could not transfer artifact org.clojars.kyp0717:my-app:jar:0.1.0-20210404.161209-1 from/to clojars (): Failed to transfer file  with status code 401
Sending org/clojars/kyp0717/my-app/0.1.0-SNAPSHOT/my-app-0.1.0-20210404.161209-1.pom (2k)
    to 
Could not transfer artifact org.clojars.kyp0717:my-app:pom:0.1.0-20210404.161209-1 from/to clojars (): Failed to transfer file  with status code 401
Failed to deploy artifacts: Could not transfer artifact org.clojars.kyp0717:my-app:jar:0.1.0-20210404.161209-1 from/to clojars (): Failed to transfer file  with status code 401

Phat Ky16:04:58

I have not create a github repo. Will I need to create a github repo to deploy to clojar?

sova-soars-the-sora16:04:11

Hmmm, and you made a group on clojars?

Dave A16:04:24

I think @sova is right, you need to use deploy tokens:

Dave A16:04:50

https://github.com/clojars/clojars-web/wiki/Pushing > leiningen 2.0.0 > lein deploy clojars > This will prompt you for your username and password*. For your password, you will need to create one or more https://github.com/clojars/clojars-web/wiki/Deploy-Tokens.* It is also possible to have them read from an https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md#authentication.

Phat Ky16:04:51

I will create a token. Will I need to create a github repo too?

Dave A16:04:13

don't think so, docs don't mention github

Phat Ky16:04:30

@sova Yes. I believe this is my group on clojars ...https://clojars.org/groups/org.clojars.kyp0717

Billy18:04:59

Hiya, is there a way of simplifying these nested if statements (my code):

(defn main [severity]
  (println (str "We're")
    (if (= severity :mild)
      "mildly inconvenienced."
      (if (= severity :severe)
        "doomed."))))
into something more concise like this?:
(defn math [opr x y]
  (({"add" +, "times" *} opr) x y))
Here's where i found the more concise function https://stackoverflow.com/questions/21681374/clojure-with-multiple-if-statements/21684980#21684980

seancorfield19:04:00

cond or case will both let you remove that nesting.

seancorfield19:04:52

Or you can just use a hash map, keyed by :mild and :severe with strings as the values.

seancorfield19:04:31

(defn main [severity]
  (println "We're" (severity {:mild "mildly inconvenienced." :severe "doomed."})))

seancorfield19:04:34

@bgigurtsis

user=> (defn main [severity]
  (println "We're" (severity {:mild "mildly inconvenienced." :severe "doomed."})))
#'user/main
user=> (main :severe)
We're doomed.
nil
user=> (main :mild)
We're mildly inconvenienced.
nil
user=> (main :what)
We're nil
nil
user=>
Note the last case.

seancorfield19:04:44

Here's a variant that provides a "not found" value:

user=> (defn main [severity]
  (println "We're" (severity {:mild "mildly inconvenienced." :severe "doomed."} "OK.")))
#'user/main
user=> (main :what)
We're OK.
nil
user=>

Billy19:04:27

That is exactly what I was looking for, thank you.

seancorfield19:04:35

(kw hash-map not-found) is like (get hash-map kw not-found)

👍 3
seancorfield19:04:56

Also (hash-map kw not-found) also means (get hash-map kw not-found)

zackteo21:04:17

Hi everyone, I have 3 lists of numbers with values 0 or 30/40/50 I want to create a new list where it gives me true (or 1) when the 3 lists are all not= 0 What might be the best way to do this?

zackteo21:04:40

Wanted to do map over and but could only do

(and
  (map #(not= 0 %) [0 0 0 0 40 0])
  (map #(not= 0 %) [0 0 0 0 0 30])
  (map #(not= 0 %) [0 0 0 50 0 0]))

zackteo21:04:08

Where I don't quite understand why and here just gives me the value of the last list

seancorfield21:04:25

Per the docstring

Evaluates exprs one at a time, from left to right. If a form
  returns logical false (nil or false), and returns that value and
  doesn't evaluate any of the other expressions, otherwise it returns
  the value of the last expr. (and) returns true.

seancorfield21:04:13

@zackteo It’s not a strictly Boolean operation — because false/`nil` are “falsey” and everything else is “truthy” in Clojure.

zackteo21:04:39

Right right :thinking_face: how do I map over and then? Is that possible? I realise there's another way to solve my problem already but don't quite understand why (map and ...) does not work

raspasov22:04:04

(map and ...) does not work because and is a macro

raspasov22:04:06

A hacky way to make it work is:

(let [v1 [true false]
      v2 [true true]
      v3 [false false]]
 (mapv #(eval `(and ~@%)) [v1 v2 v3]))

raspasov22:04:29

But (eval …) is generally not recommended if another solution is possible (almost always is)

phronmophobic21:04:32

not sure what you mean by map over and. Can you provide an example output? My guess is that you want some somewhere

zackteo21:04:20

@smith.adriane I would like to be able to do this I believe

(map and
     (map #(not= 0 %) [0 0 0 0 40 0])
     (map #(not= 0 %) [0 0 0 0 0 30])
     (map #(not= 0 %) [0 0 0 50 0 0]))

pavlosmelissinos21:04:44

I think you're looking for every? , @zackteo

phronmophobic21:04:45

like this?

(map (fn [& xs]
       (every? identity xs))
     (map #(not= 0 %) [1 0 0 0 40 0])
     (map #(not= 0 %) [1 0 0 0 0 30])
     (map #(not= 0 %) [1 0 0 50 0 0]))
;; (true false false false false false)

☝️ 3
phronmophobic21:04:04

I changed the example slightly so that there would be at least one true result

zackteo21:04:28

Wow okay okay. Never saw the need to use every? until now 😮

pavlosmelissinos21:04:19

I think this gives the same result but I'm not 100% sure

(map (fn [& xs]
       (not-any? #(= 0 %) xs))
     [1 0 0 0 40 0]
     [1 0 0 0 0 30]
     [1 0 0 50 0 0])

seancorfield22:04:50

user=> (map #(not-any? zero? %&)
     [1 0 0 0 40 0]
     [1 0 0 0 0 30]
     [1 0 0 50 0 0])
(true false false false false false)
Using zero? is more idiomatic than #(= 0 %) and then you can use # for the anonymous mapped function (`%&` is not used very often but it’s a good fit here)

💯 9
zackteo22:04:13

Also, could I ask with two lists [43 51 37 41] and [true false true false] How do I get something like [43 0 37 0] where it returns me the value if it is true and 0 if false?

zackteo22:04:30

My first instinct if any is using for but not too sure actually

Lu22:04:11

You can map over both colls simultaneously and have a condition to either return the num or 0 if false

zackteo22:04:36

How do I choose which coll to access in a map ? 😮

raspasov22:04:07

(mapv
 (fn [x1 x2]
  (if (true? x2) x1 0))
 [43 51 37 41]
 [true false true false])
=> [43 0 37 0]

raspasov22:04:22

x1 is each item in the first coll, x2 is each item in the second coll

raspasov22:04:35

(fn […]) should take a number of arguments equal to the number of collections you’re going over at the same time

zackteo22:04:10

Ohhh okay okay! I was thinking about that. Cause the functions I have been using in map seems to like work uniformly across the colls like +

zackteo22:04:21

Thanks everyone for your help!! 😄

👍 6
zackteo22:04:01

Learnt quite a fair bit from all the responses!

👌 3
lightsaber 3