Fork me on GitHub
#beginners
<
2021-05-23
>
practicalli-john09:05:44

I think I need some clojure regex help. I'd like to remove a pattern of characters toward the end of a string. For example "persons name ABC12345" would return "persons name". If there is a space after the in the string the pattern would not be removed. I've been experimenting with re-seq, replace and re-pattern but havent found the right regex. Also, any good articles on using boundaries in regex would be useful, as I suspect defining boundaries might help

indy10:05:08

(defn something [s] 
   (if-let [match (re-matches #"^(persons name) *\S+" s)]
     (second match)
     s))

indy10:05:53

(something "persons name *ABC12345") => "persons name"
(something "persons name * ABC12345") => "persons name * ABC12345"

indy10:05:02

Pretty sure it can be done more succinctly using a better capturing group and re-find

noisesmith16:05:45

a note: there's no such thing as a "clojure regex" - on the jvm clojure uses java regex, in cljs clojure uses javascript regex - this is the best cross platform regex guide I've found and I return to it often: http://www.regular-expressions.info/ - it has great comparison of the featureset / syntax of various regex systems (from grep, ed, sed, java, perl, c ...)

noisesmith16:05:36

bad site design / UI, but top quality info that's well organized once you figure out how it's meant to be navigated

Michaël Salihi09:05:34

Hi! If I want to concat 3 items: 1. A simple hash map {:url url :label "Previous"} 2. A vector of hash maps [{:url (str url item) :label (str item)}{:url (str url item) :label (str item)}...] return by a for loop 3. A simple hash map {:url url :label "Previous"} Is there a better way - more idiomatic - to do this than to surround simple maps with a vector like I do now?

(let [previous-link {:url url :label "Previous"}
      links (for [item (range 1 (inc page-number))]
              {:url (str url item) :label (str item)})
      next-link {:url url :label "Next"}]
  (concat [previous-link] links [next-link]))

indy10:05:51

Not really better or more idiomatic than what you have here,

(flatten [previous-link links next-link])

indy10:05:59

What would've been idiomatic is to have something like

(->> some-list
     (cons el2)
     (push el3))
But push doesn't exist for lists since it's O(n).

Michaël Salihi13:05:51

Perfect @UMPJRJU9E, thanks. I prefer your flatten solution that mine. 👍

Azzurite13:05:00

how do I do get the same result as (rest (rest something)) without looking so stupid? 😄

dharrigan13:05:21

(nthrest something 2)

dharrigan13:05:15

user=> (def something [1 2 3 4 5 6 7 8])
#'user/something
user=> (rest (rest something))
(3 4 5 6 7 8)
user=> (nthrest something 2)
(3 4 5 6 7 8)

dharrigan13:05:02

This talks about it more detail, in particular the difference between nthrest and drop, with drop being lazy.

Azzurite13:05:49

and another thing I've been trying to solve for a while but can't do it, is there a way to get rid of the duplication in this macro code:

(if docstring
  `(rum/defc
     ~sym
     ~docstring
     ~'< reactive
     [email protected]
  `(rum/defc
     ~sym
     ~'< reactive
     [email protected]))
I can't do the if inside because then the if gets added to the output...

Azzurite14:05:01

well after spending another 30 min on this I feel stupid...

(let [definition `(rum/defc
                    ~sym
                    ~'< reactive
                    [email protected])]
  (if docstring
    (concat 
      (take 2 definition)
      [docstring]
      (drop 2 definition))
    definition))

kennytilton11:05:46

That looks brittle. How about using [email protected] splicing with the value (when docstring [docstring])? [email protected] on nil will not inject a nil or anything else. I think. :)

dabrazhe14:05:48

How can I transform this structure in the flat vector of vectors, without loosing the inner vectors and the order? [[[1 2 3]][[12 13 14][10 50 60]]]

indy15:05:38

(->> [[[1 2 3]] [[12 13 14] [10 50 60]]]
      flatten
      (partition-all 3))

indy15:05:31

(this would return a lazy sequence)

Ivan Koz16:05:22

for variable size vectors

(defn my-flatten 
  [coll] 
  (filter (comp number? first) 
          (tree-seq (fn [[x :as c]] 
                        (and (sequential? c) 
                             ((complement number?) x))) 
                    seq
                    coll)))

;; (my-flatten [[[1 2]][[12 13 14][10 50 60 70]] [] [[[[1]]]]])
;; => ([1 2] [12 13 14] [10 50 60 70] [1])

dabrazhe17:05:08

The goal is to transform to a flat vector like this, [[1 2 3] [12 13 14] [10 50 60]] but it can’t be partitioned , because there be can be varied maps instead of numbers. Aren’t there simpler solutions eg with zipmap?

flowthing17:05:01

user=> (into [] cat [[[1 2 3]] [[12 13 14] [10 50 60]]])
[[1 2 3] [12 13 14] [10 50 60]]

👍 3
Ivan Koz18:05:54

doesn't work for deeply nested elements, 2 levels is the max

flowthing18:05:33

Yes, if the input sequence has deeper nesting, that won't work. The original question doesn't say anything about that, though.

Ivan Koz18:05:46

true, it's just for me the deep nesting is in the context of flatten

Ivan Koz18:05:43

also unintended behavior for vectors on the first level

(into [] cat [[3 4 5] [6 [7]]])
=> [3 4 5 6 [7]]

flowthing18:05:54

Whether it's unintended depends on what you need. 🙂

dabrazhe20:05:01

Does what I was looking for. There’s little documentation on the cap function. What does it do?

dabrazhe20:05:27

Actually I should have used (apply concat coll), works as well

flowthing05:05:41

> Does what I was looking for. There’s little documentation on the cap function. What does it do? cat is a transducer (https://clojure.org/reference/transducers). I like to think of it as just the cat part of mapcat or concat. > Actually I should have used (apply concat coll), works as well Yeah, there are many ways to skin this cat (pun intended, I guess): https://web.archive.org/web/20190925191622/http://chouser.n01se.net/apply-concat/ apply concat works, but doesn't return a vector. You can vec it, of course, but then there's little reason to use it over into [] cat. If you don't actually need a vector, then you can also do either (sequence cat [[[1 2 3]] [[12 13 14] [10 50 60]]]) to get a lazy seq or (eduction cat [[[1 2 3]] [[12 13 14] [10 50 60]]]) to get an eduction, depending on what you need. Transducers are nice in that they are performant and they compose well. But yes, apply concat is another option, as is mapcat identity, or mapcat seq, etc.

❤️ 3
noisesmith16:05:11

> doesn't work for deeply nested elements, 2 levels is the max if you actually need this, tree-seq is probably the easy way to do it

(->> [[1 2 3] [[4 5 6]] [[[7 8 9]]]]
     (tree-seq coll? identity)
     (filter #(and (vector? %)
                   (number? (first %)))))
([1 2 3] [4 5 6] [7 8 9])

noisesmith16:05:47

of course you can throw vec on the end if the result needs to be a vector

Ivan Koz16:05:57

isn't that what i did above?

noisesmith16:05:42

my apologies - my eyes blur and I lose cognitive function when I see "flatten" in code

Ivan Koz17:05:17

no worries, yours is much cleaner

noisesmith17:05:10

oh I see now, you put the filtering logic inside the tree-seq child predicate

Ivan Koz17:05:55

yes, i prefer your way, shows that i lack experience =)

indy17:05:47

@U051SS2EU mind enlightening why flatten is that bad? :)

noisesmith17:05:14

In clojure it's an antipattern because it's so common to use vanilla sequential structures to hold data, and a flatten (as in clojure.core/flatten) call in your processing eliminates this structure.

noisesmith17:05:51

(unless the sequential data is inside a hash-map or whatever to dead-end the recursive flatten)

noisesmith17:05:17

flatten is extremely slow and tends to encourage / facilitate poor design

indy17:05:30

Got it, time to play with tree-seq and clojure.walk

dabrazhe21:05:11

Thank you all, great help and discussion

Dieter18:05:58

Hi, can someone help to transform a async/await js example into cljs? I use shadow-cljs, installed an npm package and required it in a namespace (package beckhoff-js https://www.npmjs.com/package/beckhoff-js ). The example is:

const AdsClient = require('beckhoff-js');

const options = {
  target: {
    host: "172.16.21.6",
    netID: "5.9.36.191.1.1",
    amsPort: 801
  }
};

const client = new AdsClient.default(options);
client
  .connect()
  .then(async () => {
    // Read a tag
    const bTest = await client.readTag(".bTest");
    console.log('bTest value is', bTest);
  });
The cljs version so far:
(ns app
  (:require [reagent.core :as r]
            [reagent.dom :as rdom]
            ["beckhoff-js" :as adsclient]))

(comment

  (def client (adsclient/default. #js{:target #js{:host "127.0.0.1" :netID "172.26.0.1.1.1" :amsPort 851}}))

  (.connect client)) 
Creating the client instance works fine. The .connect returns #object[Promise [object Promise]], but the browser console returns an error: Uncaught (in promise) TypeError: net_1.Socket is not a constructor... I had a look at cljs js promise interop and tried some things with .then, but no success