How do I filter elements from a sequence based on indexes
我有一个序列
简单的例子:
1 | (filter-by-index '(a b c d e f g) '(0 2 3 4)) ; => (a c d e) |
我的用例:
1 | (filter-by-index '(c c# d d# e f f# g g# a a# b) '(0 2 4 5 7 9 11)) ; => (c d e f g a b) |
您可以使用
1 2 3 | (defn filter-by-index [coll idxs] (keep-indexed #(when ((set idxs) %1) %2) coll)) |
使用显式recur和lazy-seq的另一个版本:
1 2 3 4 5 6 7 8 | (defn filter-by-index [coll idxs] (lazy-seq (when-let [idx (first idxs)] (if (zero? idx) (cons (first coll) (filter-by-index (rest coll) (rest (map dec idxs)))) (filter-by-index (drop idx coll) (map #(- % idx) idxs)))))) |
制作一个包含与索引结合的项的向量列表,
1 2 3 4 | (def with-indexes (map #(vector %1 %2 ) ['a 'b 'c 'd 'e 'f] (range))) #'clojure.core/with-indexes with-indexes ([a 0] [b 1] [c 2] [d 3] [e 4] [f 5]) |
过滤此列表
1 2 3 4 | lojure.core=> (def filtered (filter #(#{1 3 5 7} (second % )) with-indexes)) #'clojure.core/filtered clojure.core=> filtered ([b 1] [d 3] [f 5]) |
然后删除索引。
1 2 | clojure.core=> (map first filtered) (b d f) |
然后我们将其与"最后线程"宏一起线程化
1 2 3 4 5 6 7 | (defn filter-by-index [coll idxs] (->> coll (map #(vector %1 %2)(range)) (filter #(idxs (first %))) (map second))) clojure.core=> (filter-by-index ['a 'b 'c 'd 'e 'f 'g] #{2 3 1 6}) (b c d g) |
故事的寓意是将其分解成小的独立部分,对其进行测试,然后将其组合成一个有效的功能。
最简单的解决方案是使用
1 2 | (defn filter-by-index [coll idx] (map (partial nth coll) idx)) |
我喜欢Jonas的答案,但是这两个版本都不适合无限索引序列:第一个试图创建无限集,而后者则通过在彼此之上叠加太多未实现的惰性序列而陷入堆栈溢出。为了避免这两个问题,您必须做些更多的手工工作:
1 2 3 4 5 6 7 8 9 | (defn filter-by-index [coll idxs] ((fn helper [coll idxs offset] (lazy-seq (when-let [idx (first idxs)] (if (= idx offset) (cons (first coll) (helper (rest coll) (rest idxs) (inc offset))) (helper (rest coll) idxs (inc offset)))))) coll idxs 0)) |
使用此版本,
1 2 | user> (nth (filter-by-index (range) (iterate #(+ 2 %) 0)) 1e6) 2000000 |
编辑:不试图找出乔纳斯的答案:其他解决方案都不能用于无限索引序列,这就是为什么我觉得需要一个解决方案。
我有一个类似的用例,并提出了另一个简单的解决方案。这一个期望向量。
我已经更改了函数名称以匹配其他类似的clojure函数。
1 2 | (defn select-indices [coll indices] (reverse (vals (select-keys coll indices)))) |
我知道这不是要问的问题,但是在阅读了这些答案之后,我意识到在我自己的个人用例中,我真正想要的基本上是用面具过滤。
所以这是我的看法。希望这会帮助其他人。
1 2 3 4 5 | (defn filter-by-mask [coll mask] (filter some? (map #(if %1 %2) mask coll))) (defn make-errors-mask [coll] (map #(nil? (:error %)) coll)) |
用法
1 2 3 4 5 6 | (let [v [{} {:error 3} {:ok 2} {:error 4 :yea 7}] data ["one""two""three""four"] mask (make-errors-mask v)] (filter-by-mask data mask)) ; ==> ("one""three") |
1 2 3 4 5 | => (defn filter-by-index [src indexes] (reduce (fn [a i] (conj a (nth src i))) [] indexes)) => (filter-by-index '(a b c d e f g) '(0 2 3 4)) [a c d e] |
1 2 3 4 5 6 7 8 9 | (defn filter-by-index [seq idxs] (let [idxs (into #{} idxs)] (reduce (fn [h [char idx]] (if (contains? idxs idx) (conj h char) h)) [] (partition 2 (interleave seq (iterate inc 0)))))) (filter-by-index [\\a \\b \\c \\d \\e \\f \\g] [0 2 3 4]) =>[\\a \\c \\d \\e] |