Как фильтровать элементы из последовательности на основе индексов
У меня есть последовательность s
и список индексов в эту последовательность indexes
. Как сохранить только элементы, заданные с помощью индексов?
Простой пример:
(filter-by-index '(a b c d e f g) '(0 2 3 4)) ; => (a c d e)
Моя информация:
(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
составить список векторов, содержащих элементы, объединенные с индексами,
(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])
отфильтровать этот список
lojure.core=> (def filtered (filter #(#{1 3 5 7} (second % )) with-indexes))
#'clojure.core/filtered
clojure.core=> filtered
([b 1] [d 3] [f 5])
затем удалите индексы.
clojure.core=> (map first filtered)
(b d f)
то мы вносим его вместе с макросом "thread last"
(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)
Мораль этой истории состоит в том, чтобы разбить ее на небольшие независимые части, проверить их, а затем составить в рабочую функцию.
Ответ 2
Вы можете использовать keep-indexed
:
(defn filter-by-index [coll idxs]
(keep-indexed #(when ((set idxs) %1) %2)
coll))
Другая версия, использующая явный recur и lazy-seq:
(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))))))
Ответ 3
Мне нравится ответ Джонаса, но ни одна версия не будет работать хорошо для бесконечной последовательности: первая пытается создать бесконечный набор, а последний работает в переполнении стека слишком много слоев нереализованные ленивые последовательности друг над другом. Чтобы избежать обеих проблем, вам нужно сделать несколько более ручную работу:
(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))
В этой версии обе coll
и idxs
могут быть бесконечными, и у вас все равно не будет проблем:
user> (nth (filter-by-index (range) (iterate #(+ 2 %) 0)) 1e6)
2000000
Изменить: не пытаться выделить Йонас ответ: ни одно из других решений не работает для бесконечных последовательностей, поэтому я почувствовал, что решение, которое нужно, необходимо.
Ответ 4
Самое простое решение - использовать map
:
(defn filter-by-index [coll idx]
(map (partial nth coll) idx))
Ответ 5
У меня был аналогичный вариант использования и вышло еще одно простое решение. Этот ожидает векторов.
Я изменил имя функции для соответствия другим аналогичным функциям clojure.
(defn select-indices [coll indices]
(reverse (vals (select-keys coll indices))))
Ответ 6
(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]
Ответ 7
=> (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]