4clojure #63 Group a Sequence

4clojure #63 Group a Sequence

関数fとシーケンスsを与えてマップを返す関数を作る。 キーはsの各項にfをapplyした値です。 各キーに対応する値はsの順番で連続する項の続くベクターです。

※ group-byは使用禁止。

(= (__ #(> % 5) [1 3 6 8]) {false [1 3], true [6 8]})
(= (__ #(apply / %) [[1 2] [2 4] [4 6] [3 6]])
   {1/2 [[1 2] [2 4] [3 6]], 2/3 [[4 6]]})
(= (__ count [[1] [1 2] [3] [1 2 3] [2 3]])
   {1 [[1] [3]], 2 [[1 2] [2 3]], 3 [[1 2 3]]})

まず、mapで各seqfを作用させて、結果と元の値とのリストを作り ソートして結果ごとに小グループを作る。

((fn my-gr [f coll]
   (partition-by first
       (sort-by first 
          (map #(list (f %) %) coll))))
 #(> % 5) [1 3 6 8])
 ;=> (((false 1) (false 3)) ((true 6) (true 8)))

あとは、求める形式に表現を変更するだけ。

(into {}
  (map #(hash-map (first (first %))
                    (into [] (map second %)))
    '(((false 1) (false 3)) ((true 6) (true 8)))))
 ; => {false [1 3], true [6 8]}

group-byの定義には劣るけれども自力でとけたのでよかったです。

((fn my-gr [f coll]
    (into {}
  (map #(hash-map (first (first %))
                    (into [] (map second %)))
            (partition-by first
              (sort-by first 
                (map #(list (f %) %) coll))))))
 #(> % 5) [1 3 6 8])

参考 group-byの定義

(defn group-by 
  "Returns a map of the elements of coll keyed by the result of
  f on each element. The value at each key will be a vector of the
  corresponding elements, in the order they appeared in coll."
  {:added "1.2"
   :static true}
  [f coll]  
  (persistent!
   (reduce
    (fn [ret x]
      (let [k (f x)]
        (assoc! ret k (conj (get ret k []) x))))
    (transient {}) coll)))