Sandy
Sandy

Reputation: 839

Parsing lines of a file with mixed types in Clojure

Say I have a file with lines that look like this apple,pie,100,1000. From each line I would like to have a vector like this ["apple" "pie" 100 1000], where the first two are strings and the second to are integers or Longs or whatever.

A solution could be

(defn foo [line]
  (let [[a b c d] (split line #",")]
    [a b (Integer/parseInt c) (Integer/parseInt d)]))

which does what I want, but I'm not sure if there may be a better (or more idiomatic) way to do this or not.

From this question What's the easiest way to parse numbers in clojure? , I found out about clojure.edn, so I could use read-line like this

(map read-string (clojure.string/split "apple,pie,12,134" #","))
;=> (:apple :pie 12 134)

but that gives me symbols instead of strings.

This question Convert a sequence of strings to integers (Clojure) is also similar, except that the lines contain only numbers and not numbers and strings.

Note: clojure-csv (https://github.com/davidsantiago/clojure-csv) and clojure.data.csv (https://github.com/clojure/data.csv) don't appear to have a function that automatically does this.

Upvotes: 2

Views: 657

Answers (3)

Michał Marczyk
Michał Marczyk

Reputation: 84369

You could use data.csv to parse your data and Prismatic's schema to coerce field values to the desired types:

;; [org.clojure/data.csv "0.1.2"]
;; [prismatic/schema "0.2.2"]

(require '[schema.core :as s]
         '[schema.coerce :as coerce]
         '[clojure.data.csv :as csv])

(def field-schemas [s/Str s/Str s/Int s/Int])
(def field-coercers
  (mapv coerce/coercer
    field-schemas
    (repeat coerce/string-coercion-matcher)))

(defn coerce-fields [fields]
  (mapv #(%1 %2) field-coercers fields))

And finally:

(map coerce-fields (csv/read-csv "apple,pie,12,134"))
;= (["apple" "pie" 12 134])

Upvotes: 3

Thumbnail
Thumbnail

Reputation: 13483

Not very elegant, but

(map numberise (clojure.string/split "apple,pie,12,134" #","))
; ("apple" "pie" 12 134)

where

(defn numberise [s]
  (try (Integer/parseInt s)
       (catch NumberFormatException nfe s)))

Upvotes: 0

KobbyPemson
KobbyPemson

Reputation: 2539

I think there is a library out there that makes this very easy. Can't remember the name of it but it worked like so

(let [structure [str str int int]]
 (map #(% %2) 
      structure
      (map read-string (clojure.string/split "apple,pie,12,134" #",")))
;=> ("apple" "pie" 12 134)

Upvotes: 0

Related Questions