如果不区分大小写的匹配,从字符串数组中删除字符串的惯用方法是什么?
我需要保留结果的大小写(我总是希望保留第一次出现不敏感匹配)。
简单示例:
(distinct-case-insensitive ["fish" "Dog" "cat"] ["FISH "DOG"])
将返回
["fish" "Dog" "cat"]
答案 0 :(得分:4)
这是我提出的解决方案。为简化功能,它只接受一个带有重复项的列表,因此如果您之前需要vararg列表(apply concat lists)
。
(defn distinct-case-insensitive [xs]
(->> xs
(group-by clojure.string/lower-case)
(vals)
(map first)))
(distinct-case-insensitive ["fish" "Dog" "cat" "Fish" "DOG"]) =>
("fish" "Dog" "cat")
但是,正如Leonid所说,由于hashmap,它不会保留顺序。对于有序的解决方案使用
(defn distinct-case-insesitive [xs]
(->> xs
(group-by clojure.string/lower-case)
(#(map % (map clojure.string/lower-case xs)))
(map first)
(distinct)))
答案 1 :(得分:3)
显然,你不能在这里使用内置distinct,所以你应该自己重新实现它。
mishadoff's solution非常漂亮且非常棒,但当有超过8个独特元素染色以隐藏HashMap实现时,它打破了元素的顺序。
最安全的方法是使用reduce
:
(defn concat-distinct [& colls]
(first
(reduce (fn [[coll seen] el]
(let [lc-el (string/lower-case el)]
(if (contains? seen lc-el)
[coll seen]
[(conj coll el) (conj seen lc-el)])))
[[] #{}]
(apply concat colls))))
如果适用于任意数量的集合:
user=> (concat-distinct ["fish" "Dog" "cat"] ["FISH" "DOG"] ["snake"] ["CaT" "Camel"])
["fish" "Dog" "cat" "snake" "Camel"]
对于任何数量的不同元素(与mishadoff'解决方案不同):
user=> (concat-distinct ["g" "h" "i" "j" "a" "b" "c" "d" "e" "f"])
["g" "h" "i" "j" "a" "b" "c" "d" "e" "f"]
在大多数情况下,贪婪的解决方案你会好起来的。但如果你想让它变得懒惰,那么你就无法避免递归:
(defn lazy-concat-distinct [& colls]
((fn step [coll seen]
(lazy-seq
(loop [[el & xs :as s] coll]
(when (seq s)
(let [lc-el (string/lower-case el)]
(if (contains? seen lc-el)
(recur xs)
(cons el (step xs (conj seen lc-el)))))))))
(apply concat colls) #{}))
此解决方案使用延迟序列:
user=> (def res (lazy-concat-distinct (lazy-seq (println :boo) ["boo"])))
user=> (count res)
:boo
1
使用lazy-cat
宏可以使它变得更加懒惰:
(defmacro lazy-concat-distinct* [& colls]
`(lazy-concat-distinct (lazy-cat ~@colls)))
现在它甚至不会评估它的论据,直到它们被实际使用:
user=> (def res (lazy-concat-distinct* (do (println :boo) ["boo"])))
user=> (count res)
:boo
1
当您想要从某个大型数据库聚合数据而不立即下载所有数据时,它非常有用。
N.B。小心懒惰的解决方案。例如,这个解决方案比贪婪的解决方案慢了近4倍。
答案 2 :(得分:1)
这是一个满足您要求的解决方案(第一个匹配项“获胜”并保留订单),是懒惰的,并且具有更高阶函数的好处。它需要keyfn
作为其第一个参数,与... sort-by
和group-by
。
(defn distinct-by [keyfn coll]
(letfn [(step [xs seen]
(lazy-seq
((fn [xs]
(when-let [[x & more] (seq xs)]
(let [k (keyfn x)]
(if (seen k)
(recur more)
(cons x (step more (conj seen k)))))))
xs)))]
(step coll #{})))
所以你的用法是:
(require '[clojure.string :as str])
(distinct-by str/lower-case ["fish" "Dog" "cat" "Fish" "DOG"])
;=> ("fish" "Dog" "cat")
使用recur
和内部匿名函数是一个相对较小的优化。 clojure.core/distinct
使用它,但在许多情况下没有必要。这是一个没有额外噪音的版本:
(defn distinct-by [keyfn coll]
(letfn [(step [xs seen]
(lazy-seq
(when-let [[x & more] (seq xs)]
(let [k (keyfn x)]
(if (seen k)
(step more seen)
(cons x (step more (conj seen k))))))))]
(step coll #{})))
答案 3 :(得分:0)
解决方案是实现distinct-by
,允许在检查重复项之前指定要应用于每个元素的函数
(defn distinct-by [f coll]
(let [groups (group-by f coll)]
(map #(first (groups %)) (distinct (map f coll)))))
对于示例案例,可以像
一样使用(distinct-by clojure.string/lower-case
(concat ["fish" "Dog" "cat"] ["FISH" "DOG"]))
; => ("fish" "Dog" "cat")