Clojure Vectorの重複を置き換える

ベクトル内の重複を空の文字列に置き換えようとしています。しかし、私が見つけることができる唯一の機能はそれらを置き換えるのではなく、重複を削除することです。どうやって服用できますか

["2016年10月" "2016年10月" "2016年11月" "2016年11月" "2016年11月" "2016年11月"]

そして出力:

["2016年10月" "" "2016年11月" "" "" ""]

私が見つけることができるものはすべて ["Oct 2016" "Nov 2016"] を返します。私は現在、入れ子にした doseq を実行することによって所望の出力を達成しています。これを達成するためのより良い方法はありますか? ありがとうございます。

3

5 答え

これが解決策の戦略です。

  1. loop over the items of the vector.
  2. Maintain a set of visited items. It can be used to check for uniqueness.
  3. For each item: if the set contains the current item then insert "" into the result vector.
  4. If the current item is unique then insert it into the result vector and also the set.
  5. Return the result vector when all items are visited.
  6. Optionally: Use a transient result vector for better performance.

コード:

(defn duplicate->empty [xs]
  (loop [xs     (seq xs)
         result []
         found #{}]
        (if-let [[x & xs] (seq xs)]
          (if (contains? found x)
            (recur xs (conj result "") found)
            (recur xs (conj result x) (conj found x)))
          result)))

それを呼ぶ:

(duplicate->empty ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])
=> ["Oct 2016" "" "Nov 2016" "" "" ""]
5
追加された
ベクトルの初期作成中に問題を修正することになったが、これは同様にうまくいき、解決策としてマークされた。
追加された 著者 Zaden,

完全性のためだけのトランスデューサー版。

(defn empty-duplicates
  ([]
   (fn [rf]
     (let [seen (volatile! #{})]
       (fn
         ([] (rf))
         ([res] (rf res))
         ([res x]
          (if (contains? @seen x)
            (rf res "")
            (do (vswap! seen conj x)
                (rf res x))))))))
  ([coll]
   (sequence (empty-duplicates) coll)))

(comment

  (def months ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])

  (into [] (empty-duplicates) months) ;=> ["Oct 2016" "" "Nov 2016" "" "" ""]

  )
2
追加された
(defn eliminate-duplicates [v]
        (let [result (transient (vec (repeat (count v) "")))
              index-of-first-occurences (apply merge-with #(first %&) (map-indexed (fn [x y] {y x}) v))]
            (doall (for [[s pos] index-of-first-occurences]
                       (assoc! result pos s)))
            (persistent! result)))
1
追加された

基本的には上記と同じですが、遅延シーケンス生成を使用します。

(defn rdups
  ([items] (rdups #{} items))
  ([found [x & xs :as items]]
   (when (seq items)
     (if (contains? found x)
       (lazy-seq (cons "" (rdups found xs)))
       (lazy-seq (cons x (rdups (conj found x) xs)))))))

user> (rdups ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])
;;=> ("Oct 2016" "" "Nov 2016" "" "" "")
1
追加された
lazy-seq(受け入れられたansのように)を使いますPLUSはループを落とします。私にとって最もきれいな方法です。
追加された 著者 Phil Cooper,

iterate を使うことができます:

(def months ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])

(defn step [[[head & tail] dups res]]
  [tail
   (conj dups head)
   (conj res (if (dups head)
               ""
               head))])

(defn empty-dups [xs]
  (->> (iterate step [xs #{} []])
       (drop-while (fn [[[head] _ _]] head))
       (map #(nth % 2))
       first))

(empty-dups months)
;; => ["Oct 2016" "" "Nov 2016" "" "" ""]
0
追加された