Search code examples
clojurecode-readability

clojure improve readability of filter a two-dimensional list


The current code takes a input like: [[:k1 0] [:k1 1] [:k2 2]] and organize the items that have the same keyword inside a list: (([:k1 0] [:k1 1]) ([:k2 2]))

the following code works, but I feel that can be improved. The nested map #(filter looks ugly, I think I can use clojure for function to produce the same result with a elegantly code.

how can I improve readability?

(defn list-of-equals [itens]
  (let [get-key (fn [[k]] k)
        keys (->> itens (map get-key) distinct)
        pairs (map #(filter (fn [[k]]
                              (= % k)) itens)  keys)]
      pairs))

Solution

  • The problem you're facing is that you have to iterate over the list for every distinct key. If you were to use for, it might look something like this.

    (defn for-filter [items val]
      (for [i items
            :when (= (first i) (first val))]
        i))
    

    While this might be a little cleaner, using the standard library can make it more succinct. If we do a group-by operation, we can collect all the items with the same key in a single pass.

    (group-by first items)
        => {:k1 [[:k1 0] [:k1 1]], :k2 [[:k2 2]]}
    

    You can discard the keys with vals

    (vals (group-by first items))
        => ([[:k1 0] [:k1 1]] [[:k2 2]])
    

    This is a little different than your solution

     (([:k1 0] [:k1 1]) ([:k2 2])) 
    

    vs

     ([[:k1 0] [:k1 1]] [[:k2 2]]))
    

    If that's important:

    (map #(into () %) result)
    

    The final solution looks like:

    (defn list-of-equals [items]
        (->> (vals (group-by first items))
             (map #(into () %))))