Clojure分区依据:对一些元素进行分组,而其他元素保留原样

时间:2020-07-06 12:41:37

标签: clojure

是否有一种惯用的方式可以从这里出发:

[{:red 1} {:red 2} {:blue 3} {:blue 4} {:red 5} {:blue 6} {:red 7} {:red 8} {:red 9}]

到这里:

 [[{:red 1} {:red 2}] 
  [{:blue 3}] 
  [{:blue 4}] 
  [{:red 5}] 
  [{:blue 6}] 
  [{:red 7} {:red 8} {:red 9}]]

即任何连续的红色都分组在一起,但是每个蓝色都作为一个单独的组保留吗?

谢谢。

5 个答案:

答案 0 :(得分:2)

您可以使用partition-by将序列中的连续值分组:

(partition-by ffirst [{:red 1} {:red 2} {:blue 3} {:blue 4} {:red 5} {:blue 6} {:red 7} {:red 8} {:red 9}])
;; => (({:red 1} {:red 2})
;;     ({:blue 3} {:blue 4})
;;     ({:red 5})
;;     ({:blue 6})
;;     ({:red 7} {:red 8} {:red 9}))

我发现在您的情况下,您只希望对:red键进行分组。因此,我们可以为此构建特定的功能:

(partition-by #(or (contains? % :red) %) 
              [{:red 1} {:red 2} {:blue 3} {:blue 4} {:red 5} {:blue 6} {:red 7} {:red 8} {:red 9}])
;; => (({:red 1} {:red 2})
;;     ({:blue 3})
;;     ({:blue 4})
;;     ({:red 5})
;;     ({:blue 6})
;;     ({:red 7} {:red 8} {:red 9}))

答案 1 :(得分:0)

以下是一种简单的方法:

(def x [{:red 1} {:red 2} {:blue 3} {:blue 4} {:red 5} {:blue 6} {:red 7} {:red 8} {:red 9}])

(partition-by #(if (:red %) :const (rand)) x)

;; =>(({:red 1} {:red 2})
;;   ({:blue 3})
;;   ({:blue 4})
;;   ({:red 5})
;;   ({:blue 6})
;;   ({:red 7} {:red 8} {:red 9}))

partition-by的作用是在集合的每个元素上运行一个函数,当我们获得不同的值时将其拆分。在这种情况下,我们需要构造一个自定义函数,该函数基于地图的键是:red还是其他值(例如:blue)进行分支,并始终为:red返回相同的值(例如常数,例如关键字:const),但始终为:blue之类的其他键返回不同的东西(可以通过rand之类的随机函数来完成)。

我们为它提供的匿名函数#(if (:red %) :const (rand))就是这样做的-检查密钥是否为:red,然后根据该密钥发出分支返回值。

但是,正如@cfrick在评论中提到的那样,担心连续两次获得相同的随机数-极不可能,但仍然令人担忧。如果使用rand会使您感到有些不满意,则可以使用以下替代方法:使用当前时间(低至纳秒)而不是随机数,这样可以保证在顺序过程中始终能得到不同的结果:

(partition-by #(if (:red %) :const (java.time.LocalDateTime/now)) x)

答案 2 :(得分:0)

先做一些工作然后再部分撤消可能有点笨拙,但是 在边界情况下至少相对安全(:blue的值相同) 并实际上将任何非红色分开。

user=> (sequence 
         (comp 
           (partition-by #(contains? % :red)) 
           (mapcat #(if (-> % first (contains? :red)) 
                      [%] 
                (map vector %)))) 
         [{:red 1} {:red 2} {:blue 3} {:blue 3} {:red 5}])
([{:red 1} {:red 2}] 
 [{:blue 3}] 
 [{:blue 3}] 
 [{:red 5}])

答案 3 :(得分:0)

我会选择这样的东西:

(let [is-red? #(contains? % :red)]
  (transduce (comp (partition-by is-red?)                   
                 (map #(if (is-red? (first %)) [%] (map vector %))))
             into [] data))
;; [[{:red 1} {:red 2}]
;;  [{:blue 3}]
;;  [{:blue 4}]
;;  [{:red 5}]
;;  [{:blue 6}]
;;  [{:red 7} {:red 8} {:red 9}]]

另一种方法是为blue数据引入一些合成密钥,在这种情况下,index可以:

(->> data
     (map-indexed (fn [idx itm] {:key (or (contains? itm :red) idx)
                                 :itm itm}))
     (partition-by :key)
     (map (partial map :itm)))

;; (({:red 1} {:red 2})
;;  ({:blue 3})
;;  ({:blue 4})
;;  ({:red 5})
;;  ({:blue 6})
;;  ({:red 7} {:red 8} {:red 9}))

您还可以将分区键放在元数据中,以避免创建额外的记录:

(->> data
     (map-indexed (fn [idx itm]
                    (vary-meta itm assoc :part-key (or (contains? itm :red) idx))))
     (partition-by (comp :part-key meta)))

;; (({:red 1} {:red 2})
;;  ({:blue 3})
;;  ({:blue 4})
;;  ({:red 5})
;;  ({:blue 6})
;;  ({:red 7} {:red 8} {:red 9}))

答案 4 :(得分:0)

(reduce
 (fn [acc e]
   (if (and (:red e) (:red (peek (peek acc))))
     (update acc (dec (count acc)) conj e)
     (conj acc [e])))
 []
 [{:red 1} {:red 2} {:blue 3} {:blue 4} {:red 5} {:blue 6} {:red 7} {:red 8} {:red 9}])
;; => [[{:red 1} {:red 2}]
;;     [{:blue 3}]
;;     [{:blue 4}]
;;     [{:red 5}]
;;     [{:blue 6}]
;;     [{:red 7} {:red 8} {:red 9}]]