Как найти индекс элемента в векторе?

84

Есть идеи, что ????должно быть? Есть встроенный? Как лучше всего выполнить эту задачу?

(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
Джон Лоуренс Аспден
источник
Ответ Брайана на этот вопрос очевиден, но ниже cgrand и Алекс Стоддард договариваются ответить на вопрос, который я должен был задать.
Джон Лоуренс Аспден
Ничто не мешает вам задать правильный вопрос в отдельном вопросе :)
Джонатан Бенн

Ответы:

138

Встроенный:

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. Мне придется над этим поработать.
Джон Лоуренс Аспден
2
@ Джон: Да. Точка перед indexOf указывает на взаимодействие Java. Он вызывает метод indexOf в java.lang.String. java.lang импортируется по умолчанию. Дополнительные примеры см. На clojure.org/java_interop
dermatthias
25
Вызывается векторный indexOfметод, а не String:#<Method public int clojure.lang.APersistentVector.indexOf(java.lang.Object)>
vemv
44

Стюарт Хэллоуэй дал действительно хороший ответ в этом сообщении 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))
Понзао
источник
Очень хорошо! Я ожидал такого ответа.
Джон Лоуренс Аспден
2
Не то чтобы это влияло на достоинства этого ответа, но seq-utils был изменен, теперь просто clojure.contrib.seq.
Джон Лоуренс Аспден
1
@ Джон, правда, я исправил. Благодаря!
ponzao
где взять clojure.contib.seqв clojure 1.6? В списке нет библиотеки: dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go
d9k
@ d9k, «Если пространство имен clojure.contrib указано здесь, но не содержит подробностей миграции, это означает, что никто не вызвался поддерживать это пространство имен». Я добавил пример реализации для positions.
ponzao
28
(defn find-thing [needle haystack]
  (keep-indexed #(when (= %2 needle) %1) haystack))

Но я хотел бы предостеречь вас от возни с индексами: в большинстве случаев в результате получается менее идиоматичный и неудобный Clojure.

cgrand
источник
О, как мило «когда»! Я согласен с индексами в целом, но у меня есть файл csv, и имена полей указаны в заголовке, и я хочу получить поле «field» из каждой строки, поэтому я ищу «поле» вверх в заголовке, а затем разложите строки. Я могу придумать странные вещи, связанные с чередованием, но есть ли хороший способ, который не использует явные индексы, которые можно прочитать?
Джон Лоуренс Аспден
8
Когда у меня есть этот вариант использования - заголовки csv - я только что построил карту для поиска (предполагая уникальные заголовки столбцов). Тогда карта - это моя функция для поиска по индексу. (let [header-index (zipmap header-vector (iterate inc 0))] ...)
Алекс Стоддард
1
Вау. Вы ответили на вопрос, который я должен был задать!
Джон Лоуренс Аспден
3
Что ж, я бы предложил что-то очень похожее на решение Алекса. (-> строка индекса заголовка "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

lsh
источник
2
Спасибо, что разместили эту версию. Начиная с версии 1.2 вы также можете заменить (итерация inc 0) на просто (range).
dribnet
6

Я пытался ответить на свой вопрос, но Брайан опередил меня, предложив лучший ответ!

(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
Джон Лоуренс Аспден
источник
3

Вот мой вклад, использующий loopструктуру ing и возвращение 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 (объект объекта, 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)))))
Джошуа
источник
0

Я бы пошел с редуктором

(defn find-index [pred vec]
  (reduce-kv
    (fn [_ k v]
      (if (pred v)
        (reduced k)))
    nil
    vec))
надразум1
источник