Fork me on GitHub
#beginners
<
2018-01-20
>
rachna.rajput9900:01:59

I have a data structure like this: ({:number 1 :name "John"}{:number 2 :name "Harry"}{:number 3 :name ""})

rachna.rajput9900:01:18

I want to filter this based on name.

rachna.rajput9900:01:35

if name is empty. it should not be present.

rachna.rajput9900:01:53

Any idea guys. I am really new in clojure.

petterik00:01:59

@rachna.rajput99 Using :name and seq, where seq will return nil (i.e. falsey) if the collection is empty. And since strings are collections, seq can be used on strings.

(filter (comp seq :name) '({:number 1 :name "John"}{:number 2 :name "Harry"}{:number 3 :name ""}))
;; => ({:number 1, :name "John"} {:number 2, :name "Harry"})

rachna.rajput9900:01:37

Thank you @. I need to store this updated data. will this solution update the existing data structure?

seancorfield01:01:38

@rachna.rajput99 Clojure data structures are immutable so when you use filter, map, etc you get back a new data structure and the old one is not changed.

noman01:01:32

omg filtering on strings like this is so neat

sundarj00:01:36

@rachna.rajput99 (comp seq :name) is equivalent to (fn [x] (seq (:name x)), fyi

mfm05:01:06

I have some data which comes from HTML for which I'm trying to access specific values. An entry looks like this:

(def data
[{:type :element, :attrs {:name "4011334"}, :tag :a, :content nil}
{:type :element, :attrs nil, :tag :strong, :content ["Subject:"]}
{:type :element,
:attrs {:href "t.php?id=105375#4011334", :class "tr2"},
:tag :a,
:content ["has Aphex Twin ever come to Pittsburgh?"]}
" Jan 16th, 2018 at 05:24:21 pm\n            "])
It's a vector of maps and strings, and some maps are nested. I'm trying to [ab]use destructuring to minimize getters. I want the href, the Aphex Twin :content, and the date-string. I can either reach into a nested map like so:
(let [[_ _ {{:keys [href]} :attrs} date] data]
                  (pr href date))
=> "t.php?id=105375#4011334" " Jan 16th, 2018 at 05:24:21 pm\n            "
or I can use :keys to get values for some values of the nested map, one level deep.
(let [[_ _ {:keys [attrs content]} date] data]
                  (let [href (:href attrs)]
                    (pr href content date)))
The latter produces the result I want, but I don't like that it's two letstatments. Can I do all my getting inside a single let-body?

mfm05:01:14

the multiplayer-game-state example in https://clojure.org/guides/destructuring#_associative_destructuring shows getting one of the nested maps. can one do multiple?

alexmiller13:01:35

No. By replacing the lhs of the map destructuring with more destructuring, you lose the info about what to do at that level.

mfm05:01:10

in my humble opinion, you should be able to do:

(let [[_ _ {:keys [{{:keys [href]} :attrs} content]} date] data]
                  (pr href content date))
but that fails.

david47906:01:23

Can somebody explain to me what he means https://youtu.be/doZ0XAc9Wtc?t=18m38s ? I can just use a debugger fine in Cider. Was it just not available in 2016? Or its only in cider and no other clojure IDEs?

seancorfield06:01:37

The cynic in me wants to say "I bet the audience loved that talk...!"

seancorfield07:01:36

It's a sensationalist talk. It's easy to stand up at an "X Conference" and give a rousing talk about how you went from "Language X" to "Language Y" and it was horrible so you came back to "Language X"...

david47907:01:09

but he said he went to a clojure user group, so they would have told him if there is a debugger?

seancorfield07:01:50

Sure he went to a Clojure user group. He rewrote his whole app in Clojure from Ruby. And then he rewrote it again from Clojure to Ruby. shrug

seancorfield07:01:30

It's best not to give too much weight to such talks... they're great crowd-pleasers.

david47907:01:13

it wasn't the whole app, it was split into 2 services, one in Clojure and one in ruby, which he admits was a mistake for a small company regardless of the languages involved

david47907:01:27

I don't put much weight on it, but I still wonder about this particular argument

seancorfield07:01:46

@david479 Populist talks tend to skate over the facts and/or skews the facts to suit the message. Seen a lot of those.

david47907:01:11

so debuggers where available in 2016?

seancorfield07:01:27

As you say, you can use debugging in CIDER just fine. There were debugging tools in Clojure years earlier too.

seancorfield07:01:56

But they're typically not "step debuggers" in the old-fashioned sense.

seancorfield07:01:45

Personally I think debuggers are terrible tools in any language so I would never care about a statement that says "Language X doesn't have a debugger"...

seancorfield07:01:06

(and I've been programming for over 35 years in dozens of languages)

seancorfield07:01:57

Some people like step debuggers and can't seem to function without them. I've never liked them. I've used them a handful of times in a handful of languages but I just don't find them very useful.

david47908:01:40

@mfm: I think clojure destructuring is too limited and you should use core.match

david47908:01:49

e.g.

(match data [_ _ {:attrs {:href href} :content [content]} date] [href content date])
=> ["t.php?id=105375#4011334" "has Aphex Twin ever come to Pittsburgh?" " Jan 16th, 2018 at 05:24:21 pm\n            "]

david47908:01:17

but using zipper might be less brittle in case the order of the elements might change

djwelch66614:01:11

Hello, new to clojure and I am trying to write a knapsack algorithm brute forcer as a simple learning exercise. I have this code which works:

(defn knapsack [T S r]
  (if (= (reduce + r) T)     
    #{r}
    (apply set/union (for [s S] (knapsack T (disj S s) (conj r s))))))
But I was wondering, is this good clojure? How could it be improved?

donaldball15:01:03

It seems fine. A few observations is all: 1. capital symbols are not idiomatic 2. Rich seems to regard the 2 arg form of reduce as a mistake; I always supply an initial value myself. 3. I would use a transducer for the second clause nowadays:

(defn knapsack
  [t s r]
  (if (= (reduce + 0 r) t)
    #{r}
    (into #{}
          (map (fn [x]
                 (knapsack t
                           (disj s x)
                           (conj r x))))
          s)))
if only to make it easier to apply other transformations, but itโ€™s not clear this is idiomatic.

djwelch66609:01:53

I will have to look at the transducer stuff because I am not familiar with that ๐Ÿ™‚

noisesmith15:01:51

you can do multiple keys, just not inside one another

(let  [[_ _                                                                     
        {:keys  [content]                                                       
         {{:keys  [href]} :attrs}}                                              
        date] data]                                                             
  (pr href content date)) 

noisesmith15:01:34

but usually a version with multiple destructurings is more maintainable - it's cool that you can do it all in one destructure though

noisesmith15:01:17

oops, should have tagged @mfm above

erhardt.mundt19:01:20

what's the effect of quoting, say []?

noisesmith19:01:58

quoting prevents all evaluation, but [] creates a vector while reading

noisesmith19:01:08

so you get a vector, but if it has anything in it, those things are quoted

noisesmith19:01:16

+user=> ,(map type [+ :a 1])
(clojure.core$_PLUS_ clojure.lang.Keyword java.lang.Long)
+user=> ,(map type '[+ :a 1])
(clojure.lang.Symbol clojure.lang.Keyword java.lang.Long)
+user=> 

lee.justin.m20:01:48

sometimes the explanation of how lisp works sounds like doctrine of an ancient forgotten religion

noisesmith22:01:53

I might have had a typo, one moment

mfm22:01:07

@noisesmith and yeah i probably agree that it'd be more maintainable in separate steps. i was trying to do it in the first place to see if it was use or abuse : )

noisesmith22:01:12

it has nothing to do with the vector, it is about map literal syntax

mfm22:01:35

@david479 i've never used core.match! thanks for that.

noisesmith22:01:09

@mfm it was a trivial typo - the {{:keys [href]} :attrs}} should have been {:keys [href]} :attrs /tmp/foo.clj:

(def data [{:type :element, :attrs {:name "4011334"}, :tag :a, :content nil}
           {:type :element, :attrs nil, :tag :strong, :content ["Subject:"]}
           {:type :element,
            :attrs {:href "t.php?id=105375#4011334", :class "tr2"},
            :tag :a,
            :content ["has Aphex Twin ever come to Pittsburgh?"]}
           " Jan 16th, 2018 at 05:24:21 pm\n            "])
(let  [[_ _
        {:keys  [content]
         {:keys  [href]} :attrs}
        date] data]
  (prn href content date))
in the repl:
user=> (load-file "/tmp/foo.clj")
"t.php?id=105375#4011334" ["has Aphex Twin ever come to Pittsburgh?"] " Jan 16th, 2018 at 05:24:21 pm\n            "
nil

mfm22:01:03

ah! thank you @noisesmith. i had been trying different combinations of {}'s and couldn't get it. thank you again.

noisesmith22:01:34

one advantage of doing separate destructures instead of shoving it all into one, is that the result is much easier to read and think about ๐Ÿ˜„

noisesmith22:01:21

also, putting multiple destructures into one left hand form isnโ€™t a preformance optimization - the code that destructure expands to is quite unwieldy, in fact a simple optimization is often to replace a destructure with an explicit data access

mfm22:01:56

ha. now having looked at it, i agree!

noisesmith23:01:44

the cool thing is we have a clojure.core/destructure function that shows us the expansion directly

user=> (destructure '[[_ _
        {:keys  [content]
} :attrs}
         {:keys  [href]} :attrs}
        date] data])
[vec__184 data _ (clojure.core/nth vec__184 0 nil) _ (clojure.core/nth vec__184 1 nil) map__187 (clojure.core/nth vec__184 2 nil) map__187 (if (clojure.core/seq? map__187) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__187)) map__187) map__188 (clojure.core/get map__187 :attrs) map__188 (if (clojure.core/seq? map__188) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__188)) map__188) href (clojure.core/get map__188 :href) content (clojure.core/get map__187 :content) date (clojure.core/nth vec__184 3 nil)]

noisesmith23:01:21

fixed to have normal let-block layout:

[vec__184 data
 _ (clojure.core/nth vec__184 0 nil)
 _ (clojure.core/nth vec__184 1 nil)
 map__187 (clojure.core/nth vec__184 2 nil)
 map__187 (if (clojure.core/seq? map__187)
            (clojure.lang.PersistentHashMap/create (clojure.core/seq map__187))
            map__187)
 map__188 (clojure.core/get map__187 :attrs)
 map__188 (if (clojure.core/seq? map__188)
            (clojure.lang.PersistentHashMap/create (clojure.core/seq map__188))
            map__188)
 href (clojure.core/get map__188 :href)
 content (clojure.core/get map__187 :content)
 date (clojure.core/nth vec__184 3 nil)]

mfm23:01:13

oh neat! that's a little surprising: it decomposes to nth and get calls and if statements.

noisesmith23:01:10

the primary distinction between let and let* (and fn vs. fn*) is the that version you normally use is a macro that expands its binding block using destructure before feeding the result to the more basic one