Search code examples
parsingclojure

Clojure fill map using into


I need to fill map {:v '[], :f '[]), where v-array contains data from line that starts with v. How can I do this?

Example of file:

# comment
v 1.234 3.234 4.2345234
v 2.234 4.235235 6.2345
f 1 1 1

Expected result:

{:v [(1.234 3.234 4.2345234) (2.234 4.235235 6.2345)]
 :f [(1 1 1)] }

My try:

(defn- file-lines
  [filename]
  (line-seq (io/reader filename)))

(defn- lines-with-data
  [filename]
  (->>
    (file-lines filename)
    (filter not-empty)
    (filter #(not (str/starts-with? % "#")))))

(defn- create-model
  [lines]
  (doseq [data lines]
    (into {:v '[] :f '[]}
          (->>
            (let [[type & remaining] data]
              (case type
                "v" [:v remaining]
                "f" [:f remaining]))))))

(defn parse
  [filename]
  (->>
    (lines-with-data filename)
    (map #(str/split % #"\s+"))
    (create-model)))

Exception:

user=> (p/parse "test.obj")

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)

(parse should be return result map)


Solution

  • I can repeat your error message like this:

    (into {:v '[] :f '[]} [:v [:a :b]])
    

    I can do what I think the program requires like this:

    (assoc {:v '[] :f '[]} :v [:a :b])
    ;; => {:v [:a :b], :f []}
    

    Here I just used [:a :b] instead of remaining. In your case remaining is a sequence of numbers, but it doesn't really matter.

    As for the error message it is quite a bad one, that should be gone in coming versions of Clojure. Usually it is because you are presenting map with a keyword rather than something that seq can be called on. Here the message is coming from somewhere a bit deeper than I can comprehend.

    Another construction that would work, and is I think what you wanted, is:

    (into {:v '[] :f '[]} [[:v [:a :b]]])
    

    into a map requires a sequence of map-entry. You forgot to wrap the single map-entry you were trying to place into the map. Result from my REPL:

    {:v [:a :b], :f []}