简体   繁体   中英

Clojure grouping by multiple keys

I'm having a problem when using group-by. Here is the code

(defn combine-by-coords
  [values]
  (let [grouped-by-x (group-by :x values)]
     (persistent!
     (reduce (fn [res x-val]
               (assoc! res x-val (group-by :y (x-val grouped-by-x))))
             (transient {})
             (keys grouped-by-x)))))

Where the values of the map are of the form

{:x 754, :y 56, :someKey "zxyf" ....} .

The purpose of the code would be to group maps with the same x and y values together. First I group the x values together by using the built in group-by function, which results in

{754 [{....}, {....}]} 

After that I would group the array of the key 754 by their y value. However this is where I get an error. It doesn't seem to be possible to use the key 754. This is the error I receive:

java.lang.Integer cannot be cast to clojure.lang.IFn

I've also tried the (keyword name) function to make a key out of it but this doesn't work either.

Does anybody know a solution to this problem or perhaps knows a way to rewrite my code? In the end I would just need the maps with same x and y to be grouped together.

You can't use numbers like keywords. You have to use get .

user=> (5 {5 :x})
#<CompilerException java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn (REPL:1)>
user=> ({5 :x} 5)
:x
user=> (get {5 :x} 5)
:x

In your case: exchange (x-val grouped-by-x) with (get grouped-by-x x-val) .

Or you could get rid of the x-val (the key):

(defn combine-by-coords
  [values]
  (let [grouped-by-x (group-by :x values)]
     (persistent!
     (reduce (fn [res [x-key vals]]
               (assoc! res x-key (group-by :y vals)))
             (transient {})
             grouped-by-x))))

Try using #(= (key %) 754) instead of just 754 as the first argument to group-by .

The problem is that the first argument to group-by needs to be a function that returns true for the values you want to group by. Keywords can be used as functions so these work fine, but of course an integer isn't a function, so we need to write a function that does the comparison.

Note that this allows a lot of flexibility eg

Clojure> (group-by even? (range 10))
{true [0 2 4 6 8], false [1 3 5 7 9]}

UPDATE: It seems this isn't the issue with the code in question, but I though I'd leave this answer in case anyone has a similar problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM