Welcome! Please see the About page for a little more info on how this works.

0 votes
in Sequences by

Hello, after reading words in from a file and reducing to a map:

{aabdgoo (abogado), aaceilmnotx (exclamation), ehpryz (zephyr), eirt (tire rite), ghirt (right)}

and then filtering, I have this:

([aabdgoo (abogado)] [aaceilmnotx (exclamation)] [eirt (tire rite)] [ghirt (right)])

I can run functions like vals and keys and further reductions on this with no problem.

I wanted to sort the word list in the second position of each vector and thought it would be trivial to map over this structure but after hours of iteration I can't get it to work.

(map #('((first %) (second %))) input) throws Execution error (ClassCastException) on the line of the map: class clojure.lang.PersistentList cannot be cast to class clojure.lang.IFn (clojure.lang.PersistentList and clojure.lang.IFn are in unnamed module of loader 'app').

(map #([(first %) (second %)]) input) makes the tooling complain that Vector can only be called with 1 arg but was called with: 0.

(map #(vector (first %) (second %)) input) throws Execution error (ClassCastException) a couple of lines later when I call keys on the map output: class clojure.lang.PersistentVector cannot be cast to class java.util.Map$Entry (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.util.Map$Entry is in module java.base of loader 'bootstrap').

I don't know what it wants. What am I doing wrong?

Thank you

1 Answer

+1 vote
by

You can turn that list of pairs (vectors) into a map with (into {} ...) (which also lets you use transducers, check them out). And then you'll be able to use update-vals.

(map #('((first %) (second %))) input) throws

The reader expands that #(...) into (fn* [p1__9#] ((quote ((first p1__9#) (second p1__9#))))) - you can check with (macroexpand-1 '#(...)).

The result of (quote ...) there is a list, and then you're calling that list, which is where the exception comes from.

(map #([(first %) (second %)]) input) makes the tooling complain

A similar thing - #(...) expands into something that immediately tries to call the result of [...] as a function with no arguments.

(map #(vector (first %) (second %)) input) throws Execution error (ClassCastException) a couple of lines later

Treating a map as a sequence gives you a sequence of kv-pairs with a type that implements java.util.Map$Entry. The keys function is supposed to be called on a map, not on a random sequence, and it just happens to work with a sequence of kv-pairs, but it expects for each pair to be a proper instance of that Map$Entry interface.
By using that (vector ...) you're turning each map entry into a vector that doesn't implement that interface.

Some generic advice:

  • For kv-pairs, use key and val instead of first and second - they also expect a map entry and will fail much earlier if something's wrong, making figuring stuff out easier. They're also faster and more indicative what the code's intent
  • If you have problems with #(...), try writing it as (fn [...] ...) with properly named arguments (so e.g. not x but kv-pair - doesn't sound like much but helps with figuring stuff out if you're stuck)
  • If you have ClassCastException, carefully read the docs of the functions that you're calling
  • When you see an exception, study its stacktrace and try to figure out what exactly went wrong. Might be hard at first but will become a breeze later, at least in most cases
...