user468687
user468687

Reputation:

Find index of an element matching a predicate in Clojure?

With Clojure, how do I find the first index with a positive value in this vector [-1 0 3 7 9]?

I know you can get the first result of something rather elegantly with first and filter:

(first (filter pos? [-1 0 99 100 101]))

This code returns the value 99. The answer I want is the index which is 2.

Upvotes: 31

Views: 9804

Answers (7)

watofundefined
watofundefined

Reputation: 374

(defn first-idx
  ([pred]
   (fn [coll] (first-idx pred coll)))
  ([pred coll]
   (reduce-kv
     (fn [_ idx val] (when (pred val) (reduced idx)))
     nil
     coll)))

(first-idx nil? [:a :b nil])       ;; ~> 2
((first-idx nil?) [:a :b :c nil])  ;; ~> 3
(first-idx nil? nil)               ;; ~> nil
(first-idx nil? [:a :b :c])        ;; ~> nil

Upvotes: 0

solussd
solussd

Reputation: 494

I'm a little late to the party, but I prefer:

(defn index-of-pred
  [pred coll]
  (ffirst (filter (comp pred second) (map-indexed list coll))))

;; example usage
(index-of-pred pos? [-1 -2 -5 0 3 4 1 -100])
;=> 4

Upvotes: 1

BillRobertson42
BillRobertson42

Reputation: 12883

(defn pred-idx [pred [idx hist] cur]
  (if (pred cur)
    [(inc idx) (conj hist idx)]
    [(inc idx) hist]))

(defn idx-filter [pred col]
  (second (reduce (partial pred-idx pred) [0 []] col)))

(first (idx-filter pos? [-1 0 99 100 101]))
2

Not sure if this is better, but it works. I think it forces evaluation of the entire sequence though, and if you need all indices that would be better. The correct thing to do is probably turn it into a lazy sequence somehow, but I'm done for the evening.

Upvotes: 1

user1118608
user1118608

Reputation:

(first (filter #(not (nil? %)) (map #(when (pos? %1) %2) [-1 1 0 99 100 101] (range))))

Map can take one or more collections and return one list,put condition on map,and filter nil.

Upvotes: 1

Óscar López
Óscar López

Reputation: 236004

Try this:

(defn first-index
  ([pred coll] (first-index coll pred 0))
  ([pred coll idx]
    (cond (= coll '()) -1
          (pred (first coll)) idx
          :else (recur pred (rest coll) (inc idx)))))

And use it like this:

(defn is-pos? [x]
  (> x 0))

(first-index is-pos? [-1 0 3 7 9])

It returns the zero-based index of the first element that satisfies the predicate (is-pos? in the example), or -1 if no element matches the predicate.

Upvotes: 0

Jonas
Jonas

Reputation: 19642

Using keep-indexed you can get a sequence of indices for which a predicate is satisfied:

(defn indices [pred coll]
   (keep-indexed #(when (pred %2) %1) coll))

With this simple function you'll solve your problem with the expression

user=> (first (indices pos? [-1 0 99 100 101]))
2

Note that, due to the lazyness of keep-indexed (and indices), the entire sequence need not be realized so no extraneous calculations are performed.

Upvotes: 48

Samiur
Samiur

Reputation: 76

(defn first-pos [x] 
  (loop [arr x n 0]
     (if (pos? (first arr))
     n
     (recur (next arr) (inc n)))))

This is a good example of using functional programming's powerful tail recursion.

Upvotes: 3

Related Questions