Fork me on GitHub
#clojure
<
2018-11-07
>
leira00:11:10

I'm trying to (merge-with -) with 2 maps, the idea is, if one key is not exist in the first map, it should be treated as value 0. But merge-with doesn't do that, it only applies the - function on values exist in both maps.

leira00:11:25

Is there any way to achieve that?

mfikes00:11:23

If you know all the expected keys, you could merge a map with all zero values with the first map, and use merge-with in that result and the second map

leira00:11:35

No~ more like, I should add all the keys in map2 missing from map1 with value 0 into map1 first, before merge-with

leira00:11:40

but that seems ugly...

lilactown00:11:47

yeah, unfortunately merge-with doesn’t even execute the passed in fn if the key doesn’t exist in both

hiredman00:11:12

or leave the values out and learn about fnil

lilactown00:11:40

hehe, I tried (merge-with (fnil - 0) {:a 1 :b 2} {:a 2 :c 3}) - doesn’t work

hiredman00:11:00

no, I mean, leave the default 0 value out of the maps

hiredman00:11:14

and when you update them using update-in, use fnil to fill in the default values

polymeris00:11:05

something like (map #(f (get m1 % 0) (get m2 % 0)) (clojure.set/union (keys m1) (keys m2)))?

polymeris00:11:14

not very nice either I guess

mfikes00:11:43

I think Leira, was describing that the desired semantics are something like

(merge-with f (merge (zipmap (keys m2) (repeat 0)) m1) m2)

leira00:11:04

this is nice~!

leira00:11:32

wait, it doesn't work it will update teh keys already in m1 as 0s

lilactown00:11:47

if you modify the source of merge-with you can get what you want I think

lilactown00:11:54

(defn merge-always-with [f & maps]
  (when (some identity maps)
    (let [merge-entry (fn [m e]
                        (let [k (key e) v (val e)]
                          (assoc m k (f (get m k) v))))
          merge2 (fn [m1 m2]
                   (reduce merge-entry (or m1 {}) (seq m2)))]
      (reduce merge2 maps))))

leira00:11:00

yeah, one way to do it~

lilactown00:11:19

user=> (merge-always-with - {:a 1} {:b 2})
NullPointerException   clojure.lang.Numbers.ops (Numbers.java:1018)
user=> (merge-always-with (fnil - 0) {:a 1} {:b 2})
{:a 1, :b -2}

jaawerth00:11:06

not as elegant as the above, but how about

mfikes00:11:14

@leira (merge (zipmap (keys m2) (repeat 0)) m1) leaves existing entries in m1 alone

jaawerth00:11:32

the zipmap way is way better if it matches the desired behavior though

jaawerth00:11:26

I think you'd want it to handle keys that show up in any subsequent maps, not just the second, though

leira00:11:44

@mfikes Oh, yes, the zipmap was before m1, my bad, this is a good solution~

mfikes00:11:16

Well, at least it is a starting point, to describe the semantics clearly

leira00:11:16

I really like the atmosphere here You guys are so helpful thanks~!

enforser00:11:20

(reduce-kv #(update %1 %2 (fnil - 0) %3) m1 m2)
would something like that work?

jaawerth00:11:29

yeah and you could update the zipmap approach to grab the keys from all additional maps

mfikes01:11:54

Nice solution @trailcapital. I suspect that variant also performs really well.

enforser01:11:52

that's what I was thinking! I also popped it into a function to accept many maps and merge them all tail recursively.

(defn merge-with*
  [f init & ms]
  ((fn [m maps]
    (if (empty? maps)
      m
      (recur (reduce-kv #(update %1 %2 f %3) m (first maps))
             (rest maps))))
   init ms))

enforser01:11:51

It would be cool if reduce accepted n number of arguments and applied that sort of recursive processing...

(defn- recursive-reduce
  [f i & a]
  ((fn [m args]
     (if (empty? args)
       m
       (recur (reduce f m (first args))
              (rest args))))
   i a))

=> (recursive-reduce conj [] [1 2 3] [4 5 6] [7 8 9])
[1 2 3 4 5 6 7 8 9]

jaawerth02:11:09

@trailcapital there's always (partial transduce cat) so long as you wrap your reducing function with completing

💯 4
jaawerth02:11:55

you could even optimize that version with transient though that's probably overkill...

kyicy08:11:37

When generics involved, how to implement this Java function in Clojure?

didibus09:11:01

@zhongtao_liu You can just ignore the generics

didibus09:11:10

(defn value [point]
  (Pair. value jacobian))

schmee09:11:17

@zhongtao_liu you might want to check out https://neanderthal.uncomplicate.org/ for blazing fast native Clojure matrix math stuff

🎉 4
☝️ 4
kyicy09:11:58

@didibus surprise, surprise, it works! Thanks. @schmee Great library, We'll have a try.

octahedrion10:11:51

are there any examples of the use of Datafy ?

jaawerth20:11:50

@octo221 None I know of but playing with it I think I get the idea: it more or less serializes things to EDN, and you can use the Datafiable protocol to specify custom behavior

jaawerth20:11:17

e.g. (defrecord Foo [a b] clojure.core.protocols/Datafiable (datafy [self] "Sorry, you can't have my data!"))

jaawerth20:11:24

whereas without extending it, it'll fall back to the standard behavior of #path.to.Foo{:a aval, :b bval}

ghadi20:11:48

it's weird to implement it on a record, which is already data

ghadi20:11:04

would make more sense on a deftype

ghadi20:11:49

Note that this isn't about serialization (EDN is notation) but to in-memory data like maps lists vectors sets scalars...

seancorfield20:11:41

I'm wondering if Stu or Rich will talk about the new stuff they're doing on top of prepl and datafy at Conj?

seancorfield20:11:25

Stu sort of hinted on the Apropos podcast that he'd be talking about some new tooling he's been using -- and Alex has basically said prepl etc all happened because of something Rich is building.

orestis21:11:09

Rich’s keynote is titled “Maybe Not” which kinda sounds like a type system thing?

seancorfield22:11:31

Given Rich's previous comments about "type systems", I doubt it... 🙂

jaawerth20:11:46

@ghadi yeah good points both. record was just my arbitrary example but that's a good point about serialization

jaawerth20:11:53

prepl looks like an awesome way to build out tooling

nickmbailey21:11:39

anyone using the "lein-git-version" plugin?

nickmbailey21:11:13

trying to get it set up but just generating a "clojure.lang.Keyword cannot be cast to java.base/java.lang.String" exception

arrdem00:11:11

Author here. Happy to help if you paste your config.