如何在向量中找到项的索引?

有什么想法 ????应该是什么? 有一个内置的吗? 完成这项任务的最佳方法是什么?

(def v ["one" "two" "three" "two"])


(defn find-thing [ thing vectr ]
(????))


(find-thing "two" v) ; ? maybe 1, maybe '(1,3), actually probably a lazy-seq
64940 次浏览

Built-in:

user> (def v ["one" "two" "three" "two"])
#'user/v
user> (.indexOf v "two")
1
user> (.indexOf v "foo")
-1

If you want a lazy seq of the indices for all matches:

user> (map-indexed vector v)
([0 "one"] [1 "two"] [2 "three"] [3 "two"])
user> (filter #(= "two" (second %)) *1)
([1 "two"] [3 "two"])
user> (map first *1)
(1 3)
user> (map first
(filter #(= (second %) "two")
(map-indexed vector v)))
(1 3)

I was attempting to answer my own question, but Brian beat me to it with a better answer!

(defn indices-of [f coll]
(keep-indexed #(if (f %2) %1 nil) coll))


(defn first-index-of [f coll]
(first (indices-of f coll)))


(defn find-thing [value coll]
(first-index-of #(= % value) coll))


(find-thing "two" ["one" "two" "three" "two"]) ; 1
(find-thing "two" '("one" "two" "three")) ; 1


;; these answers are a bit silly
(find-thing "two" #{"one" "two" "three"}) ; 1
(find-thing "two" {"one" "two" "two" "three"}) ; nil

Stuart Halloway has given a really nice answer in this post http://www.mail-archive.com/clojure@googlegroups.com/msg34159.html.

(use '[clojure.contrib.seq :only (positions)])
(def v ["one" "two" "three" "two"])
(positions #{"two"} v) ; -> (1 3)

If you wish to grab the first value just use first on the result.

(first (positions #{"two"} v)) ; -> 1

EDIT: Because clojure.contrib.seq has vanished I updated my answer with an example of a simple implementation:

(defn positions
[pred coll]
(keep-indexed (fn [idx x]
(when (pred x)
idx))
coll))
(defn find-thing [needle haystack]
(keep-indexed #(when (= %2 needle) %1) haystack))

But I'd like to warn you against fiddling with indices: most often than not it's going to produce less idiomatic, awkward Clojure.

I recently had to find indexes several times or rather I chose to since it was easier than figuring out another way of approaching the problem. Along the way I discovered that my Clojure lists didn't have the .indexOf(Object object, int start) method. I dealt with the problem like so:

(defn index-of
"Returns the index of item. If start is given indexes prior to
start are skipped."
([coll item] (.indexOf coll item))
([coll item start]
(let [unadjusted-index (.indexOf (drop start coll) item)]
(if (= -1 unadjusted-index)
unadjusted-index
(+ unadjusted-index start)))))

As of Clojure 1.4 clojure.contrib.seq (and thus the positions function) is not available as it's missing a maintainer: http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go

The source for clojure.contrib.seq/positions and it's dependency clojure.contrib.seq/indexed is:

(defn indexed
"Returns a lazy sequence of [index, item] pairs, where items come
from 's' and indexes count up from zero.


(indexed '(a b c d))  =>  ([0 a] [1 b] [2 c] [3 d])"
[s]
(map vector (iterate inc 0) s))


(defn positions
"Returns a lazy sequence containing the positions at which pred
is true for items in coll."
[pred coll]
(for [[idx elt] (indexed coll) :when (pred elt)] idx))


(positions #{2} [1 2 3 4 1 2 3 4]) => (1 5)

Available here: http://clojuredocs.org/clojure_contrib/clojure.contrib.seq/positions

Here's my contribution, using a looping structure and returning nil on failure.

I try to avoid loops when I can, but it seems fitting for this problem.

(defn index-of [xs x]
(loop [a (first xs)
r (rest xs)
i 0]
(cond
(= a x)    i
(empty? r) nil
:else      (recur (first r) (rest r) (inc i)))))

I'd go with reduce-kv

(defn find-index [pred vec]
(reduce-kv
(fn [_ k v]
(if (pred v)
(reduced k)))
nil
vec))

We don't need to loop the whole collection if we need the first index. The some function will short circuit after the first match.

(defn index-of [x coll]
(let [idx? (fn [i a] (when (= x a) i))]
(first (keep-indexed idx? coll))))