Как отфильтровать элементы из последовательности на основе индексов - PullRequest
13 голосов
/ 12 октября 2011

У меня есть последовательность 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)

Ответы [ 8 ]

25 голосов
/ 12 октября 2011

Вы можете использовать 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))))))
13 голосов
/ 12 октября 2011

составляет список векторов, содержащих элементы, объединенные с индексами,

(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)

затем мы связываем его вместе с макросом «последний поток»

(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)

Мораль этой истории состоит в том, чтобы разбить ее на маленькие независимые части, проверить их, а затем объединить в рабочую функцию.

6 голосов
/ 01 октября 2013

Самое простое решение - использовать map:

(defn filter-by-index [coll idx]
  (map (partial nth coll) idx))
6 голосов
/ 12 октября 2011

Мне нравится ответ Джонаса, но ни одна из версий не будет хорошо работать для бесконечной последовательности индексов: первая пытается создать бесконечное множество, а вторая сталкивается с переполнением стека путем наложения слишком большого количества нереализованных ленивых последовательностей друг на друга. Чтобы избежать обеих проблем, вы должны сделать немного больше ручной работы:

(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

Редактировать: не пытаться выделить ответ Джонаса: нет из других решений работают для бесконечных последовательностей индексов, поэтому я чувствовал, что решение, которое нужно, необходимо.

1 голос
/ 07 января 2014

У меня был похожий вариант использования, и я нашел другое простое решение. Этот ожидает векторов.

Я изменил имя функции, чтобы оно соответствовало другим аналогичным функциям clojure.

(defn select-indices [coll indices]
   (reverse (vals (select-keys coll indices))))
0 голосов
/ 09 марта 2018

Я знаю, что это не то, о чем спрашивали, но после прочтения этих ответов я понял, в моем собственном личном случае, что я на самом деле хотел, это в основном фильтрация по маске.

Итак, вот мой дубль. Надеюсь, это поможет кому-то еще.

(defn filter-by-mask [coll mask]
  (filter some? (map #(if %1 %2) mask coll)))

(defn make-errors-mask [coll]
  (map #(nil? (:error %)) coll))

Использование

(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")
0 голосов
/ 01 октября 2013
=> (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]
0 голосов
/ 12 октября 2011
(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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...