Fork me on GitHub

I’m tinkering with Oz a Clojure viz library and just following the steps in the README, but when I try and render my first viz nothing loads, when I inspect the console in Chrome I see this: Error: Invalid field type "undefined". at qi (oz.js:28) at Ii (oz.js:28) at oz.js:28 at hp (oz.js:28) at new wp (oz.js:28) at jp (oz.js:28) at Object.e.compile (oz.js:28) at CB (oz.js:1205) at Object.<anonymous> (oz.js:1206) at Object.<anonymous> (oz.js:823) my code:

(ns learning-clojure.viz
  (:require [oz.core :as oz]))


(defn play-data [& names]
  (for [n names
        i (range 20)]
    {:time i :item n :quantity (+ (Math/pow (* i (count n)) 0.8) (rand-int (count n)))}))

(def line-plot
  {:data {:values (play-data "monkey" "slipper" "broom")}
   :encoding {:x {:field "time"}
              :y {:field "quantity"}
              :color {:field "item" :type "nominal"}}
   :mark "line"})

;; Render the plot
(oz/view! line-plot) 


Figured out the issue, the :field needs a :type value set otherwise it breaks


@glfinn83 Calva has a command for loading those tools into the REPL. Don't know if you knew.

calva 4

Awesome! Is it ctrl + shift c enter?


The command is named... lemme check ... Require REPL utilities, like (doc) etcetera, into Current Namespace, default keybinding is ctrl+shit+c ctrl+u. (It is mostly useful for the REPL window. In the editor most of that functionality is built into the UI.)


Thanks good sir!


This might be useful to beginners: It's 4clojure but as a clojure / babashka script, so you can run it locally.

👍 12

i have a keymap utility i’m rewriting in Clojurescript, wondering if there’s a better way to write this function:

function concatModifierKeyNames(_k: string, mods: ModifierMap) {
  let k = _k.slice()
  if (mods.alt) k = `Alt+${k}`
  if (mods.ctrl) k = `Ctrl+${k}`
  if (mods.meta) k = `Meta+${k}`
  if (mods.shift) k = `Shift+${k}`
  return k}
currently I have this:
(defn concat-modifier-keynames [k mods]
  (let [k (if (:alt mods) (str "Alt+" k) k)
        k (if (:ctrl mods) (str "Ctrl+" k) k)
        k (if (:meta mods) (str "Meta+" k) k)
        k (if (:shift mods) (str "Shift+" k) k)]
but i’m redeclaring/tossing the k variable around more than I’d like. is there a better way to conditionally concat strings like this?


@alidcastano do the modifiers have to appear in a certain order?


Yes they do


@alidcastano One thing that may help is that str will ignore nil values, and when returns nil when the condition is false.


(str (when (= 1 2) "not true") 
     (when (= 1 1) " is true") 
     (when true " always"))
=> " is true always"

👍 4
Chris O’Donnell18:03:39

I would probably use cond->>:

(defn concat-modifier-keynames [k {:keys [alt ctrl meta shift]}]
  (cond->> k
    alt (str "Alt+")
    ctrl (str "Ctrl+")
    meta (str "Meta+")
    shift (str "Shift+")))

👏 8

This is a great solution, thanks

👍 4

I'm struggling to understand how to iterate across a collection of maps. If I have a simple structure then I can use destructuring. So:

(def map2 {:name "Molly", :species "cat", :colour "grey", :age 9} )
(let [{:keys [age species]} map2] (print age species)) ;;=> 9 cat
But, if I have something more complex:
(def animals [{:name "Molly" :species "cat", :colour "grey", :age 9}
              {:name "Jack" :species "cat" :colour "ginger" :age 8}]
I can't figure out how to iterate across each map and pull out elements to use them. So far I can get it to use map to iterate across each individual map in turn. But, I can't get it to destructure the individual elements so I can use them. So far I've tried this, but it keeps saying "Unable to resolve symbol: age in this context"
(defn current_animals [animals]
            [{:keys [age species]} %]
        ) animals))
Can someone give me an idea what I'm doing wrong?


Instead of using an anonymous function:

            [{:keys [age species]} %]
Use a regular fn:
(fn [{:keys [age species]}]  ...)
The parameters of a regular fn can use destructuring.


Or you can use a let, which also allows destructuring on the left side of the binding.


ah so that works! There must be something I don't understand about the anonymous function macro:

#( .... )


It is just a short hand, and it is more limited in functionality.


It is meant to be used only for simple, short functions.


thanks @UBRMX7MT7 - been trying to get that working for ages today 🙂


@UV1JWR18U Think of the anonymous function as an extra pair of parens that calls what you have, meaning that #([...]) is actually (fn[] ([...])) , that is, you're calling the vector as a function. It's just shorthand and useful if you do want to make such a call (to say map or update or something), but remember that there's a pair of parentheses that calls the first thing in the anonymous form


Ah thanks for that, now I understand why. Yeah, so:

(macroexpand-1 '#()) ;;=> (fn* [] ())


(quote #(+ 1 %))
=> (fn* [p1__3179#] (+ 1 p1__3179#))
(quote #(+ %1 %2))
=> (fn* [p1__3184# p2__3185#] (+ p1__3184# p2__3185#))


The expansion is done by the reader rather than it being a macro.


In a book I'm reading it says that cons returns a 'logical' list, a sequence (not a list). Which exact implementation of ISeq is being returned?



(type (cons ...))
in the repl.


you'll get different things, depending on what you're consing to


Not OP, but learned something new today. I (wrongly) thought cons returns a sequence no matter what. Thanks!


It does return a sequence no matter what, as defined by seq?, just different concrete types (which shouldn't matter).

(type (cons 1 nil))
  (type (cons 1 [1]))
  (seq? (cons 1 nil))
  (seq? (cons 1 [1]))

=> clojure.lang.PersistentList
=> clojure.lang.Cons
=> true
=> true

👍 4