Assume that there is a sequence like below:
["ab" "ba" "ac" "ca" "bc" "cc"]
I want to know the frequencies but the key should be sorted string. In short, I want to get the result like this:
{"ab" 2, "ac" 2, "bc" 1, "cc" 1}
Clojure has frequencies
function, but it doesn't accept key function.
So, usually I can do this by the combination of group-by
and map
:
(->> ["ab" "ba" "ac" "ca" "bc" "cc"]
(group-by #(apply str (sort %)))
(map (fn [[k vs]] [k (count vs)]))
(int {}))
But, this looks verbose. Even in Java, I can do grouping and counting at the same time with Stream API, like this: (Assuming that there is a method sortedStr(s)
Arrays.asList("aa", "ab", "ab", "bb", "cc" , "ca")
.stream()
.collect(groupingBy(s->sortedStr(s), counting()));
Is there any way to group-by and counting at once in clojure like Java8?
Here is a Clojure version using the built-in frequencies
function.
(frequencies (map #(apply str (sort %))
["ab" "ba" "ac" "ca" "bc" "cc"]))
;;=> {"ab" 2, "ac" 2, "bc" 1, "cc" 1}
I may be wrong, but the Java version already gets the sorted keys in your example. In that case, it would be just a call to frequencies
in Clojure (if I understood your question correctly).
EDIT: Looks like the Java version was corrected in the meantime, so my last comment becomes obsolete.
@Stefan answer works well, but it is not the most efficient, because it first maps over the coll (producing an intermediate collection) and then finds frequencies. So it doesn't really conform to "group-by and count at once" part of your question. I would rather go with reduce
:
user> (reduce #(update %1 (apply str (sort %2)) (fnil inc 0))
{} ["ab" "ba" "ac" "ca" "bc" "cc"])
{"ab" 2, "ac" 2, "bc" 1, "cc" 1}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With