Fork me on GitHub
#clojurescript
<
2018-05-24
>
pesterhazy10:05:39

A question about data-structures: In a view I'm storing a bunch of panels in my app state (Reagent-style). Each panel has an uuid. I want to be able to add and delete panels. But I also want to be able to reach into the panel and access them by ID.

pesterhazy10:05:33

On the face of it, I have two options. I could store the panels in a vector, which means that access by ID O(n).

pesterhazy10:05:13

Or I could use a hash-map as the primary data structure. In that case, access by ID is fast, but I need to keep track of order in some other way, like another vector or using a "position" attribute.

pesterhazy10:05:56

Neither seems like a great option. Is there an approach I'm missing? What data structure is the most appropriate?

tristefigure10:05:06

Since it's UI n will be in [0..100] at most so you shouldn't be thinking about algorithmic complexity. In practice this will look like everything happens in constant time, no ? But to answer your question you can use a sorted-map (you must provide an ordering function) or an ordered-map (keeps track of the insertion order). The former is in clojure.core while the latter is at https://github.com/amalloy/ordered

tristefigure10:05:00

Not sure it support clojurescript though

pesterhazy10:05:00

@tristefigure interesting point about algorithmic complexity... Certainly a cljs.core/PersistentArrayMap is used for up to 32 elements anyway.

tristefigure11:05:09

Was this irony ? I'm definitively interested in having a conversation about when to apply algorithmic complexity. I don't know the specificities of the context you're working on but to me it doesn't look like one should care about time complexity when it comes to traditionnal ui elements since to be meaningful to the user there must be few elements. The only time where I really have to take care of algorithmic complexity is when I write optimization algorithms which has yet to happen in my professional life. And when I'm optimizing stuff at work it's never pure algorithmic complexity or at least it's masked under higher level concerns, like doing batch updates to a DB. It's never has pure as in the textbooks. The kind of complexity I always have on my mind is what I call code complexity, i.e. the complexity of the algorithm that is writing the code, ie. my tools my abstraction and I. Just yesterday, out of curiosity I rewrote almost 2000 lines of ruby in 150 lines of Clojure. As a result I could perform changes that whould have taken me 1 day to unroll in 1 minute.

pesterhazy10:05:17

Which I guess scans rather than relying on hashes

pesterhazy10:05:39

I don't think cljs.core/sorted-map supports supplying your own sort function, so the map would be ordered the UUIDs no?

thheller10:05:08

@pesterhazy typically a separate entry works best {:order [5 1 2] :entries {5 {...} 1 {...} 2 {...}}

tristefigure10:05:58

To complement your quest of datastructures there is also https://github.com/clojure/data.priority-map >A priority map is very similar to a sorted map, but whereas a sorted map produces a sequence of the entries sorted by key, a priority map produces the entries sorted by value.

thheller10:05:59

position attr works as well but depending on how often the list is updated might incur extra overhead for the sort

pesterhazy10:05:22

@tristefigure that also doesn't help for the use case I'm afraid

pesterhazy10:05:29

@thheller that's what we were leaning towards as well

pesterhazy10:05:02

I guess you could abstract this away to a certain extent

pesterhazy10:05:51

There could be versions of conj and dissoc that work on a {:order [] :entries {}} map (or defrecord)

pesterhazy10:05:30

A positional-map 🙂

thheller10:05:27

I would recommend sticking to standard stuff and don't get too fancy. just calling (-> db (assoc-in [:entries 1] {:id 1 ...}) (update :order conj 1)) is easy enough

thheller10:05:57

if you start writing custom deftypes/defrecord you have to start worrying about how they serialize

thheller10:05:08

or live-reload might blow up your state and so on

pesterhazy10:05:51

the pos-conj and pos-dissoc functions could still work on regular maps

thheller10:05:03

yep thats good

kwladyka13:05:12

I noticed whenever node_modules appear in directory I have error during start figwheel:

internal/modules/cjs/loader.js:573
    throw err;
    ^

Error: Cannot find module '@cljs-oss/module-deps'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:571:15)
    at Function.Module._load (internal/modules/cjs/loader.js:497:25)
    at Module.require (internal/modules/cjs/loader.js:626:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at [eval]:3:13
    at Script.runInThisContext (vm.js:91:20)
    at Object.runInThisContext (vm.js:298:38)
    at Object.<anonymous> ([eval]-wrapper:6:22)
    at Module._compile (internal/modules/cjs/loader.js:678:30)
    at evalScript (internal/bootstrap/node.js:542:27)
According to https://clojurescript.org/guides/javascript-modules#node-modules I added: npm i -D module-deps resolve browser-resolve But error didn’t disappear. What I miss?

kwladyka13:05:23

include NPM in cljs is very confusing to find right way

pesterhazy14:05:20

@curlyfry I didn't know about that one! But it also keeps the map sorted by the key, which would be the UUID. AIUI a custom comparator wouldn't help when all you know is the item's key

👍 4
jjttjj15:05:33

I'm trying to get cljs.main working on windows, following the instructions in the quick start (https://clojurescript.org/guides/quick-start). It works but when I add a dependency to the deps file

{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}
        com.taoensso/timbre {:mvn/version "4.10.0"}}}
and then (:require [taoensso.timbre :as log]) in the clojure namespace, and run the cljs.main command again I get
clojure.lang.ExceptionInfo: No such namespace: taoensso.timbre, could not locate taoensso/timbre.cljs, taoensso/timbre.cljc, or JavaScript source providing "taoensso.timbre"
am I missing something here? Edit: even following the last section of the Quick Start guide and adding react results in the same error. It seems like the deps aren't actually being fetched/added to the classpath?

bhauman15:05:51

@jjttjj and you are using clj -m cljs.main ?

jjttjj16:05:14

no java -cp "cljs.jar;src" cljs.main --compile game4.core --repl

jjttjj16:05:26

(due to windows)

bhauman16:05:51

so the deps file only works with clj

bhauman16:05:23

so the easiest thing to do here is use lein

jjttjj16:05:27

gotcha, thanks, wasn't sure if that was supposed to be automatic, (i kinda assumed the cljs.jar had the same features as cljs.main)

jjttjj16:05:07

or rather clj

bhauman16:05:23

unfortunately no

bhauman16:05:37

but you can do this:

bhauman16:05:21

lein run -m cljs.main if you set up a leiningen project

jjttjj16:05:41

cool thanks for the info

bhauman16:05:13

then lein new myproject

bhauman16:05:45

@pesterhazy I personally group the position/rank into a larger structure because once you have a position you most certainly will have other metadata that will follow

pesterhazy16:05:45

@bhauman interesting, what other kind of metadata are you thinking of?

bhauman16:05:00

catagorical

bhauman16:05:21

sometimes display these catagories

bhauman16:05:26

sometimes those

bhauman16:05:48

but its often good to group the data, sort by position and then (map :element )

pesterhazy16:05:17

one thing I'm worried about is inserting an element between two existing elements

bhauman16:05:05

again there will be no computational complexity to worry about here

pesterhazy16:05:11

with a rank attribute, you need to using floats to squeeze in between [1 2], [1 1.5] etc., with the obvious problem that you may run out of floating point precision

bhauman16:05:44

no you will just do a simple insert where you update all the positions

pesterhazy16:05:46

the alternative would be to re-number the elements, which touches all elements

bhauman16:05:17

yes thats the way to do it as long as you are not dealing with tons of elements

pesterhazy16:05:01

right, that may always be fast enough

bhauman16:05:25

way way faster than the rerender

pesterhazy16:05:49

"selected" may be another piece of meta-data to track in the wrapper map

bhauman16:05:04

exactly, and you are off to the races

mg18:05:10

Anyone had this problem before, or any ideas?

user> (require '[figwheel-sidecar.repl-api :as ra])
CompilerException java.lang.Exception: namespace 'cljs.closure' not found, compiling:(cljs/repl.cljc:9:1) 

mg18:05:13

@souenzzo A different googling strategy got me to a useful answer - old guava was coming in from another dep. Fixed now. Thanks

👍 4
idiomancy20:05:23

anyone know how the :open-file-command key in figwheel works? I'd prefer to just have it use a file from my project rather than something in my home directory. Everything tells me to put the file in my ~/bin directory

idiomancy20:05:37

but why can't I use something from, e.g. resources

bhauman20:05:06

@idiomancy :open-file-command just needs a path, I'm not sure off the top of my head if that path can be relative to the project directory

idiomancy20:05:46

thanks, yeah I just was having trouble finding the source, so I didn't know what the path was relative to

whilo21:05:14

i am porting clojure code to cljs and have heard that recently the macro referral has been simplified

whilo21:05:33

do you have a pointer for me how to do it best now?

whilo21:05:00

the code uses a CPS transform and is a bit macro heavy

thheller21:05:34

@whilo https://clojurescript.org/guides/ns-forms#_implicit_sugar. basically just a cljs ns that has a require-macros for itself.

whilo22:05:10

@thheller thanks! i have actually learnt about this from your podcast 🙂

thheller22:05:56

hmm I guess we need to advertise this better. it has actually been around for about 3 years now. surprised it is not common knowledge by now. https://dev.clojure.org/jira/browse/CLJS-948

whilo22:05:20

so i still need to require-macros my own namespace in cljs if it contains macros in clj (in a cljc file), but otherwise it should just work?

whilo22:05:29

(the same as in clojure that is)

thheller22:05:04

yes, only your CLJS ns needs to require-macros itself. others don't.

whilo22:05:35

good, thanks

johnj23:05:31

does 'this' creates trouble for cljs users?