7
\$\begingroup\$

I've got a Clojure function here that's meant to parse a string of form:

"DDXXXYYY"

where DD is meant to be discarded, and XXX and YYY are string representations of integers.

(defn split-id [tileid]
  (map #(Integer/parseInt %)
       (map (partial apply str)
            (partition 3 (drop 2 tileid)))))

Or, written with the threading macro:

(defn split-id [tileid]
  (map #(Integer/parseInt %)
       (map (partial apply str)
            (->> tileid (drop 2) (partition 3)))))

Does anyone have any recommendations for a simple way to do this? It feels like clojure.contrib.string should have a partition function that takes a string and an integer.

I think I've come up with a fairly good block of code for the above function, which I had to extend a bit. I'd love comments and suggestions on the final block.

(defn parse-ints
  "Converts all strings in the supplied collection to their integer
  representation."
  [coll]
  (map #(Integer/parseInt %) coll))

(def res-map
  ^{:doc "Map between the second digit in a MODIS TileID
metadata value and the corresponding resolution."}
  {:1 "1000"
   :2 "500"
   :4 "250"})

(defn tileid->res
  "Returns a string representation of the resolution referenced by the
supplied MODIS TileID."
  [tileid]
  (res-map (keyword (subs tileid 1 2))))

(defn tileid->xy
  "Extracts integer representations of the MODIS X and Y coordinates
referenced by the supplied MODIS TileID."
  [tileid]
  (parse-ints
   (map (partial apply str)
        (partition 3 (subs tileid 2)))))

(defn split-id
  "Returns a sequence containing the resolution, X and Y
  coordinates (on the MODIS grid) referenced by the supplied MODIS
  TileID."
  [tileid]
  (flatten
   ((juxt tileid->res
          tileid->xy) tileid)))
\$\endgroup\$
6
\$\begingroup\$

Alternative solution is to keep it really simple:

(defn extract-int [s start end]
  (Integer/parseInt (subs s start end)))

(defn split-id [s]
  (list
    (extract-int s 2 5)
    (extract-int s 5 8)))

I believe that hardcoding two calls to extract-int is a simpler and more elegant way to meet the requirement than a solution which involves a map. Sure it's nice to use abstraction with higher-order functions, but YAGNI principle applies.

\$\endgroup\$
2
\$\begingroup\$

You could try the following, using the Java substring method and a vector of offsets:

(defn split-id [tileid]
  (map 
    #(Integer. (.substring tileid % (+ % 3)) ) 
    [2 5]))
\$\endgroup\$
0
\$\begingroup\$

Just a though, perhaps you're looking at this the wrong way. This is a parsing question, and simple parses are often best executed through regular expressions. Clojure has reader macros and special functions to work with Java regexes.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.