2011-01-28 14 views
66

Qualche idea di cosa dovrebbe essere ????? C'è un built-in? Quale sarebbe il modo migliore per eseguire questa attività?Come trovo l'indice di un oggetto in un vettore?

(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 
+0

Brian's è chiaramente la risposta a questa domanda, ma sotto, cagnolino e Alex Stoddard cospirano per rispondere alla domanda che avrei dovuto chiedere. –

+0

Niente ti impedisce di porre la domanda corretta in una domanda separata :) –

risposta

107

Built-in:

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

Se si desidera un seguenti pigra degli indici per tutte le partite:

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) 
+1

Dolce, grazie Brian, il mio ricercatore di documenti non ha trovato indexOf, presumibilmente perché è Java. Dovrò lavorarci sopra. –

+1

@ John: Sì. Il punto prima indexOf indica l'interoperabilità Java. Chiama il metodo 'indexOf' in java.lang.String. java.lang viene importato di default. Per altri esempi vedere http: // clojure.org/java_interop – dermatthias

+19

È il metodo 'indexOf' del vettore che viene chiamato, non String's:' # ' – vemv

3

stavo cercando di rispondere alla mia domanda, ma Brian battito io ad esso con una risposta migliore!

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

Stuart Halloway ha dato una risposta davvero bello in questo post 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) 

Se desiderate afferrare il primo valore basta usare first sul risultato.

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

EDIT: Perché clojure.contrib.seq è svanito Ho aggiornato la mia risposta con un esempio di una semplice implementazione:

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

Molto bello! Questo è il tipo di risposta che mi aspettavo. –

+2

Non che influisce sul merito di questa risposta, ma seq-utils è stato modificato solo clojure.contrib.seq ora. –

+1

@ John, vero, l'ho risolto. Grazie! – ponzao

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

Ma vorrei mettervi in ​​guardia contro giocherellare con gli indici: il più delle volte piuttosto che produrre meno clojure idiomatico e imbarazzante.

+0

Oh bello "quando"! Sono d'accordo sugli indici in generale, ma ho un file csv, i nomi dei campi sono nell'intestazione, e voglio ottenere il campo "campo" da ogni riga, quindi quello che sto facendo è cercare "campo" su nell'intestazione, e poi nthing le righe. Posso pensare a cose strane da fare con l'interleave, ma c'è un modo carino che non usa indici espliciti leggibili? –

+8

Quando ho quel caso d'uso - header csv - ho appena creato una mappa per fare la ricerca (assumendo intestazioni di colonne univoche). La mappa _è quindi la mia funzione per fare la ricerca dell'indice. (lascia [header-index (zipmap header-vector (iterate inc 0))] ...) –

+0

Wow. Hai risposto alla domanda che avrei dovuto chiedere! –

2

Recentemente ho dovuto trovare gli indici più volte o meglio ho scelto perché era più facile che capire un altro modo di affrontare il problema. Lungo la strada ho scoperto che le mie liste di Clojure non avevano il metodo .indexOf (Object object, int start). Ho affrontato il problema in questo modo:

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

Al Clojure 1.4 clojure.contrib.seq (e quindi la funzione positions) non è disponibile in quanto manca un manutentore: http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go

La fonte di clojure.contrib.seq/positions ed è la dipendenza 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) 

Disponibile qui: http://clojuredocs.org/clojure_contrib/clojure.contrib.seq/positions

+2

Grazie per aver postato questa versione. Dalla 1.2 puoi anche sostituire (iterare inc 0) con semplicemente (intervallo). – dribnet

2

Ecco il mio contributo, utilizzando una struttura loop e restituendo nil in caso di errore.

Cerco di evitare i loop quando posso, ma mi sembra appropriato per questo problema.

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