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

94

有什么想法,???? 应该是什么?有没有内置的方法? 完成这个任务的最佳方式是什么?

(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

Brian显然是这个问题的答案,但在下面,cgrand和Alex Stoddard密谋回答我本应该问的问题。 - John Lawrence Aspden
没有什么能阻止你在另一个问题中提出正确的问题 :) - Jonathan Benn
9个回答

152

内置:

user> (def v ["one" "two" "three" "two"])
#'user/v
user> (.indexOf v "two")
1
user> (.indexOf v "foo")
-1
如果您想获得所有匹配项的索引的延迟序列:
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)

3
谢谢,布莱恩。我的文档搜索器没有找到indexOf,可能是因为它是Java的函数。我需要进一步研究一下。 - John Lawrence Aspden
2
@John:是的。indexOf 前面的点表示 Java 互操作性。它调用了 java.lang.String 中的方法 'indexOf'。java.lang 默认被导入。更多示例请参见 http://clojure.org/java_interop。 - dermatthias
26
被调用的是向量的 indexOf 方法,而不是字符串的:#<Method public int clojure.lang.APersistentVector.indexOf(java.lang.Object)> - deprecated
如果你正在进行map-indexed和filter操作,也可以看一下keep-indexed。 - Rachel K. Westmacott

49

Stuart Halloway在这篇文章中给出了一个非常好的答案: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)

如果您希望获取第一个值,只需在结果上使用first即可。

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

编辑:因为clojure.contrib.seq已经消失,所以我更新了我的答案并提供了一个简单实现的示例:

(defn positions
  [pred coll]
  (keep-indexed (fn [idx x]
                  (when (pred x)
                    idx))
                coll))

非常好!这就是我期望的答案。 - John Lawrence Aspden
2
并不影响这个答案的价值,但是seq-utils已经被更改为clojure.contrib.seq。 - John Lawrence Aspden
在Clojure 1.6中如何获取clojure.contrib.seq?列表中没有库:http://dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go - d9k
@d9k,“如果在此处列出了一个clojure.contrib命名空间,但没有迁移详细信息,则意味着没有人自愿维护该命名空间。”我为“positions”添加了一个示例实现。 - ponzao

29
(defn find-thing [needle haystack]
  (keep-indexed #(when (= %2 needle) %1) haystack))

但我想提醒你不要在索引上瞎搞:大多数情况下,这会产生不太符合惯用法的、笨拙的Clojure。


哦,很好的“when”!我同意通常关于索引的想法,但是我有一个csv文件,字段名称在标题中,并且我想从每一行获取“field”字段,所以我正在查找标题中的“field”,然后无聊的行。我可以想到使用交错的奇怪方法,但是有没有一种不使用显式索引的漂亮且易读的方法呢? - John Lawrence Aspden
8
当我需要使用csv表头时,我会构建一个映射来进行查找(假设表头唯一)。该映射成为我的索引查找函数。(使用以下代码:(let [header-index (zipmap header-vector (iterate inc 0))] ...)) - Alex Stoddard
1
哇,你回答了我应该问的问题! - John Lawrence Aspden
3
嗯,我本来会提出与Alex的解决方案非常相似的东西。 (->“colname”标题索引行),然后你就有了你的值。 - cgrand

14

从Clojure 1.4开始,clojure.contrib.seq(因此positions函数)不再可用,因为它缺少一个维护者: http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go

clojure.contrib.seq/positions和它的依赖项clojure.contrib.seq/indexed的源码是:

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

可以在此处获取:http://clojuredocs.org/clojure_contrib/clojure.contrib.seq/positions


2
感谢您发布这个版本。自从1.2版本以来,您还可以用简单的(range)替换(iterate inc 0)。 - dribnet

7

我本想回答自己的问题,但是Brian已经用更好的答案超过了我!

(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

5
这是我的贡献,使用循环结构,并在失败时返回nil
我尽量避免使用循环,但对于这个问题似乎很合适。
(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)))))

2

最近我多次需要查找索引,或者说我选择这样做是因为这比寻找其他解决问题的方法更容易。在此过程中,我发现我的Clojure列表没有.indexOf(Object object, int start)方法。我通过以下方式解决了这个问题:

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

1

如果我们只需要第一个索引,就不需要循环整个集合。 some 函数会在找到第一个匹配项后立即停止。

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

0

我会选择使用 reduce-kv

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接