14 Stimmen

Wie filtere ich Elemente aus einer Sequenz auf der Grundlage von Indizes?

Ich habe eine Sequenz s und eine Liste von Indizes in dieser Sequenz indexes . Wie behalte ich nur die über die Indizes angegebenen Elemente?

Einfaches Beispiel:

(filter-by-index '(a b c d e f g) '(0 2 3 4)) ; => (a c d e)

Mein Anwendungsfall:

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

28voto

Jonas Punkte 18752

Sie können verwenden keep-indexed :

(defn filter-by-index [coll idxs]
  (keep-indexed #(when ((set idxs) %1) %2) 
                coll))  

Eine andere Version, die explizit recur und lazy-seq verwendet:

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

13voto

Arthur Ulfeldt Punkte 89086

Eine Liste von Vektoren erstellen, die die Elemente in Kombination mit den Indizes enthalten,

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

diese Liste filtern

lojure.core=> (def filtered (filter #(#{1 3 5 7} (second % )) with-indexes))
#'clojure.core/filtered
clojure.core=> filtered
([b 1] [d 3] [f 5])

entfernen Sie dann die Indizes.

clojure.core=> (map first filtered)                                          
(b d f)

dann fädeln wir sie mit dem Makro "thread last" zusammen

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

Die Moral von der Geschicht' ist, sie in kleine unabhängige Teile zu zerlegen, sie zu testen und sie dann zu einer funktionierenden Funktion zusammenzusetzen.

10voto

Leonid Beschastny Punkte 47348

Die einfachste Lösung ist die Verwendung von map :

(defn filter-by-index [coll idx]
  (map (partial nth coll) idx))

7voto

amalloy Punkte 82950

Ich mag Jonas' Antwort, aber keine der beiden Versionen funktioniert gut für eine unendliche Folge von Indizes: die erste versucht, eine unendliche Menge zu erstellen, und die zweite führt zu einem Stapelüberlauf durch zu viele nicht realisierte faule Sequenzen überlagern übereinander. Um beide Probleme zu vermeiden, müssen Sie etwas mehr manuelle Arbeit leisten:

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

Mit dieser Version können sowohl coll y idxs kann unendlich sein und Sie werden trotzdem keine Probleme haben:

user> (nth (filter-by-index (range) (iterate #(+ 2 %) 0)) 1e6)
2000000

Edit: Ich will Jonas' Antwort nicht herausgreifen: keine der anderen Lösungen funktionieren für unendliche Indexfolgen, weshalb ich der Meinung war, dass eine solche Lösung erforderlich ist.

1voto

Paul English Punkte 937

Ich hatte einen ähnlichen Anwendungsfall und habe eine weitere einfache Lösung gefunden. Diese Lösung erwartet Vektoren.

Ich habe den Funktionsnamen geändert, um ihn an andere ähnliche Clojure-Funktionen anzupassen.

(defn select-indices [coll indices]
   (reverse (vals (select-keys coll indices))))

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X