2014-07-21 2 views
6

Esiste un metodo API della libreria Scala (e in caso contrario, un modo idiomatico) per ottenere un elenco di tutti gli indici per una sottostringa (destinazione) all'interno di una stringa più grande (origine)? Ho provato a guardare attraverso ScalaDoc, ma non sono riuscito a trovare nulla di ovvio. Ci sono così tanti metodi che fanno tante cose utili, suppongo che non stia semplicemente sottomettendo i termini di ricerca giusti.Restituisce tutti gli indici di una particolare sottostringa

Per esempio, se ho una stringa di origine di "Nome: Yo, nome: Jim, nome: nome, il nome: bozo" e io uso una stringa di destinazione di "Nome:" Mi piacerebbe tornare un Elenco [Int] di List (0, 8, 17, 27).

Ecco il mio trucco veloce per risolvere il problema:

def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = { 
    def recursive(index: Int, accumulator: List[Int]): List[Int] = { 
     if (!(index < source.size)) accumulator 
     else { 
     val position = source.indexOf(target, index) 
     if (position == -1) accumulator 
     else { 
      recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator) 
     } 
     } 
    } 

    if (target.size <= source.size) { 
     if (!source.equals(target)) { 
     recursive(0, Nil).reverse 
     } 
     else List(0) 
    } 
    else Nil 
    } 

Qualsiasi orientamento è possibile darmi sostituzione di questo con un adeguato punto di ingresso della libreria standard sarebbe molto apprezzato.

AGGIORNAMENTO 2014/Lug/22:

Ispirato dalla risposta di Siddhartha Dutta, ho tighted il mio codice. E ora si presenta così:

def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = { 
    @tailrec def recursive(indexTarget: Int, accumulator: List[Int]): List[Int] = { 
     val position = source.indexOf(target, indexTarget) 
     if (position == -1) accumulator 
     else 
     recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator) 
    } 
    recursive(index, Nil).reverse 
    } 

Inoltre, se ho una stringa di origine di "aaaaaaaa" e io uso una stringa di destinazione di "aa", vorrei per difetto di tornare un elenco [Int] di Elenco (0, 2, 4, 6) che salta una ricerca iniziando all'interno di una sottostringa trovata. L'impostazione predefinita può essere sovrascritta passando "true" per il parametro withinOverlaps che nel caso "aaaaaaaa"/"aa" restituirà List (0, 1, 2, 3, 4, 5, 6).

+1

No, non c'è "un metodo [Standard]". Inoltre, poiché questo è un codice funzionante, * potrebbe * essere più adatto per la revisione del codice. – user2864740

+0

@ chaotic3quilibrium Qualsiasi modo in cui potresti BSD Licenza quel metodo in modo che l'uomo capo non si arrabbi con me se lo copio/lo adattare? :) – ericpeters

+0

@ericpeters È a mia conoscenza che qualsiasi frammento di codice pubblicato qui su StackOverflow può essere considerato essenzialmente di dominio pubblico; Non vincolato da alcun vincolo di licenza che limita la tua capacità di tagliare/incollare/modificare/personalizzare lo snippet in qualsiasi contesto tu abbia bisogno. – chaotic3quilibrium

risposta

6

Sono sempre incline a raggiungere il sacco di trucchi regex con problemi come questo. Non direi che è corretto, ma è un codice infernale. :)

val r = "\\Qname\\E".r 
val ex = "name:Yo,name:Jim,name:name,name:bozo" 

val is = r.findAllMatchIn(ex).map(_.start).toList 

Le citazioni \\Q e \\E non sono necessari per questo caso, ma se la stringa che state cercando ha alcun carattere speciale, allora sarà.

+0

Molto bello. Ho passato meno di due minuti a valutare l'approccio regex prima di montare il mio codice Scala. È bello avere più di un modo per skinare il gatto di ricerca delle stringhe. – chaotic3quilibrium

+0

BTW, è anche possibile modificare la prima riga in "" "\ Qname \ E" "". R se si desidera utilizzare la regex pura (come copia/incolla senza escape da qualche altra fonte). L'opzione di virgolette in Scala è fantastica! – chaotic3quilibrium

1

un piccolo codice per ottenere tutti gli indici
chiamare il metodo seguito come getAllIndexes (origine, destinazione)

def getAllIndexes(source: String, target: String, index: Int = 0): List[Int] = { 
     val targetIndex = source.indexOf(target, index) 
     if(targetIndex != -1) 
      List(targetIndex) ++ getAllIndexes(source, target, targetIndex+1) 
     else 
      List() 
     } 
+0

Sembra che restituisca l'elenco in ordine inverso, ad esempio Elenco (27, 17, 8, 0), giusto? Inoltre, è possibile ottimizzare i due percorsi se. Il primo che sostituisce "List (targetIndex) ++ get ..." con "targetIndex :: get ...". E il secondo sostituisce "List()" con "Nil". – chaotic3quilibrium

+1

No il metodo restituisce l'elenco in ordine crescente come per gli indici, ad esempio, Elenco (0,8,17,27). Le ottimizzazioni sono corrette. –

+0

Ho appena provato la tua chiamata e dopo aver aggiunto l'annotazione @tailrec, sto ricevendo un errore del compilatore affermando che non è ricorsivo in coda (con ++ o: :). Tuttavia, il tuo codice più piccolo mi ha ispirato, quindi ho fornito un aggiornamento per mostrare il mio codice rafforzato. Ho anche aggiunto un altro caso di test (l'esempio "aaaaaaaa", "aa") per mostrare il vantaggio del parametro opzionale withinOverlaps. – chaotic3quilibrium