2015-02-28 10 views
13

Sono un principiante del linguaggio di programmazione funzionale e sto imparando a Scala per un progetto universitario.Scala - converti matrice [stringa] in matrice [doppia]

Questo può sembrare semplice, ma non riesco a trovare abbastanza aiuto online per questo o un modo semplice per farlo: come posso convertire un array [stringa] in matrice [doppio]? Ho un file CSV che, quando letto nel REPL, viene interpretato come valori String (ogni riga del file ha una combinazione di valori interi e stringa) che restituirebbe un tipo Array [String]. Voglio codificare i valori stringa con valori double/int per restituire l'array [Double] al fine di rendere l'array omogeneo. C'è un modo semplice per farlo? Qualsiasi guida sarà molto apprezzata.

Quello che ho fatto fino ad ora è:

def retrieveExamplesFromFile(fileName : String) : Array[Array[String]] = { 
    val items = for { 
    line <- Source.fromFile(fileName).getLines() 
    entries = line.split(",") 
    } yield entries 

    return items.toArray 
} 

Il formato di ogni riga (restituito come String []) è così:

[[1.0, 2.0, item1], [5, 8.9, item2],....] 

E per convertire ogni riga nel file CSV in doppio array, ho solo una definizione pseudo redatta in modo:

def generateNumbersForStringValues(values : Array[String]) : Array[Double] = { 
val line = for(item <- values) 
{ 
    //correct way? 
    item.replace("item1", "1.0") 
    item.replace("item2", "1.0")  
} 
return //unable to typecast/convert 
} 

Tutte le idee sono benvenute. Grazie per il tuo tempo.

risposta

22

Probabilmente si desidera utilizzare map insieme toDouble:

values.map(x => x.toDouble) 

O più conciso:

values.map(_.toDouble) 

E per il ripiego per non-corde doppie, è potrebbe prendere in considerazione l'utilizzo della monade Try (in scala.util):

values.map(x => Try(x.toDouble).getOrElse(1.0)) 

Se si conosce ciò che ogni linea sarà simile, si potrebbe anche fare pattern matching:

values map { 
    case Array(a, b, c) => Array(a.toDouble, b.toDouble, 1.0) 
} 
+0

L'istruzione getOrElse o il pattern matching funzionano meglio per me. Tuttavia, volevo sapere se posso aggiungere un contro a questo piuttosto che restituire solo 1.0 tutto il tempo? Ho sentito parlare di Range (n1, n2) ma non è quello che sto cercando, sto cercando un contatore perché voglio che item1 e item2 abbiano numeri diversi (forse consecutivi). È possibile? –

+0

Certo - puoi mettere qualunque logica tu voglia nella clausola 'getOrElse'. Ad esempio, è possibile creare un iteratore 'val fallbackIterator = Iterator.from (0)', e quindi usare 'getOrElse (fallbackIterator.next)', in modo che ogni chiamata successiva a 'getOrElse' aumenti il ​​valore di fallback. –

+0

Stavo cercando quello Prova monade: non lo sapevo in scala.util. v bella risposta qui. – javadba

3

Intendi convertire tutte le stringhe in double con un fallback a 1.0 per tutte le stringhe non convertibili? Che sarebbe:

val x = Array(
    Array("1.0", "2.0", "item1"), 
    Array("5", "8.9", "item2")) 

x.map(_.map { y => 
    try { 
    y.toDouble 
    } catch { 
    case _: NumberFormatException => 1.0 
    } 
}) 
+1

meglio per prendere il 'java.lang.NumberFormatException' per evitare l'avviso del compilatore – dhg

+0

@dhg e più importante: per evitare la cattura di eccezioni mortali come OOM e errori VM interni. – rightfold

+0

@ райтфолд, sì, ma è per questo che l'avvertimento del compilatore ... –

3

Ampliando @ commento di DaunnC, è possibile utilizzare l'utilità Try per fare questo e pattern match sul risultato in modo da poter evitare di chiamare get o avvolgendo il risultato in un Option:

import scala.util.{Try, Success, Failure} 

def main = { 
    val maybeDoubles = Array("5", "1.0", "8.5", "10.0", "item1", "item2") 

    val convertDoubles = maybeDoubles.map { x => 
    Try(x.toDouble) 
    } 

    val convertedArray = convertDoubles.map { 
    _ match { 
     case Success(res) => res 
     case Failure(f) => 1.0 
    } 
    } 

    convertedArray 
} 

Questo permette di pattern match sul risultato di Try, che è sempre o un 01.233.418,millionso Failure, senza dover chiamare get o altrimenti avvolgere i risultati.

Ecco qualche informazione in più su Prova per gentile concessione di Mauricio Linhares: https://mauricio.github.io/2014/02/17/scala-either-try-and-the-m-word.html

+0

Copia/incolla il codice in mio metodo principale e ottengo questo O/P: Success (5.0) Success (1.0) Success (8.5) Success (10,0) Failure (java.lang.NumberFormatException: Per stringa di input: "item1") Errore (java.lang.NumberFormatException: Per stringa di input: "item2") –

+0

Ah, sarebbe perché non stava producendo la mappa finale. Ho aggiornato la mia risposta. –