Jak znaleźć indeks elementu w wektorze?

84

Jakieś pomysły, co ????powinno być? Czy jest wbudowany? Jaki byłby najlepszy sposób wykonania tego zadania?

(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
John Lawrence Aspden
źródło
Brian's jest oczywiście odpowiedzią na to pytanie, ale poniżej cgrand i Alex Stoddard spiskują, aby odpowiedzieć na pytanie, które powinienem był zadać.
John Lawrence Aspden
Nic nie stoi na przeszkodzie, aby zadać poprawne pytanie w osobnym pytaniu :)
Jonathan Benn

Odpowiedzi:

138

Wbudowane:

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

Jeśli chcesz uzyskać leniwą sekwencję indeksów dla wszystkich dopasowań:

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)
Brian Carper
źródło
3
Super, dzięki Brian, moja wyszukiwarka dokumentów nie znalazła indexOf, prawdopodobnie dlatego, że jest to Java. Muszę nad tym popracować.
John Lawrence Aspden
2
@John: Tak. Kropka przed indexOf wskazuje na współpracę między Javą. Wywołuje metodę „indexOf” w java.lang.String. Domyślnie plik java.lang jest importowany. Więcej przykładów można znaleźć na stronie clojure.org/java_interop
dermatthias
25
indexOf#<Method public int clojure.lang.APersistentVector.indexOf(java.lang.Object)>
Wywoływana
44

Stuart Halloway dał naprawdę miłą odpowiedź w tym poście http://www.mail-archive.com/[email protected]/msg34159.html .

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

Jeśli chcesz pobrać pierwszą wartość, po prostu użyj firstwyniku.

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

EDYCJA: Ponieważ clojure.contrib.seqzniknął, zaktualizowałem swoją odpowiedź przykładem prostej implementacji:

(defn positions
  [pred coll]
  (keep-indexed (fn [idx x]
                  (when (pred x)
                    idx))
                coll))
ponzao
źródło
Bardzo dobrze! Takiej odpowiedzi się spodziewałem.
John Lawrence Aspden
2
Nie żeby miało to wpływ na wartość tej odpowiedzi, ale seq-utils zostało teraz zmienione tylko clojure.contrib.seq.
John Lawrence Aspden
1
@John, prawda, naprawiłem to. Dzięki!
ponzao
skąd wziąć clojure.contib.seqclojure 1.6? Brak biblioteki na liście: dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go
d9k
@ d9k, "Jeśli przestrzeń nazw clojure.contrib jest wymieniona tutaj, ale nie ma szczegółów migracji, oznacza to, że nikt nie zgłosił się na ochotnika do utrzymywania tej przestrzeni nazw." Dodałem przykładową implementację dla positions.
ponzao
28
(defn find-thing [needle haystack]
  (keep-indexed #(when (= %2 needle) %1) haystack))

Ale chciałbym was ostrzec przed majstrowaniem przy indeksach: najczęściej da to mniej idiomatyczny, niezręczny Clojure.

cgrand
źródło
Och, miło „kiedy”! Generalnie zgadzam się co do indeksów, ale mam plik csv, a nazwy pól są w nagłówku i chcę pobrać pole „pole” z każdego wiersza, więc to, co robię, to szukanie „pola” w górę w nagłówku, a następnie nthing wiersze. Mogę wymyślić dziwne rzeczy, które można zrobić z przeplotem, ale czy istnieje fajny sposób, który nie używa wyraźnych indeksów, które są czytelne?
John Lawrence Aspden
8
Kiedy mam ten przypadek użycia - nagłówki csv - właśnie zbudowałem mapę do wyszukiwania (zakładając unikalne nagłówki kolumn). Mapa jest wtedy moją funkcją do przeszukiwania indeksu. (let [header-index (zipmap header-vector (iterate inc 0))] ...)
Alex Stoddard
1
Łał. Odpowiedziałeś na pytanie, które powinienem był zadać!
John Lawrence Aspden
3
Cóż, zaproponowałbym coś bardzo podobnego do rozwiązania Alexa. (-> wiersz nagłówka-indeksu „nazwa_kolumny”) i masz swoją wartość.
cgrand
14

Od Clojure 1.4 clojure.contrib.seq (a tym samym positionsfunkcja) nie jest dostępna, ponieważ nie ma opiekuna: http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go

Źródłem clojure.contrib.seq/positionsi zależnością clojure.contrib.seq/indexedjest:

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

Dostępne tutaj: http://clojuredocs.org/clojure_contrib/clojure.contrib.seq/positions

lsh
źródło
2
Dziękujemy za opublikowanie tej wersji. Od wersji 1.2 można również zamienić (iteruj przyrost 0) na po prostu (zakres).
drybling
6

Próbowałem odpowiedzieć na własne pytanie, ale Brian pobił mnie lepszą odpowiedzią!

(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
John Lawrence Aspden
źródło
3

Oto mój wkład, wykorzystujący loopstrukturę ing i powracający nilpo niepowodzeniach.

Staram się unikać pętli, kiedy mogę, ale wydaje się, że pasuje to do tego problemu.

(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)))))
Josh.F
źródło
2

Ostatnio musiałem kilka razy znaleźć indeksy, a raczej zdecydowałem się na to, ponieważ było to łatwiejsze niż wymyślanie innego sposobu podejścia do problemu. Po drodze odkryłem, że moje listy Clojure nie mają metody .indexOf (Object object, int start). Z problemem poradziłem sobie tak:

(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)))))
Joshua
źródło
0

Poszedłbym z redukcją kv

(defn find-index [pred vec]
  (reduce-kv
    (fn [_ k v]
      (if (pred v)
        (reduced k)))
    nil
    vec))
overermind1
źródło