How to prevent Clojure exception: clojure.lang.LazySeq cannot be cast to clojure.lang.IFn

StackOverflow https://stackoverflow.com/questions/8769387

  •  14-04-2021
  •  | 
  •  

Question

I am trying to pass the (lazy) sequence returned from a map operation to another map operation, so that I can look up elements in the first sequence. The code is parsing some football fixtures from a text file (in row/column format), cleaning it up, and then returning a map.

Here is the code:

(ns fixtures.test.lazytest
  (:require [clojure.string :as str])
  (:use [clojure.test]))

(defn- column-map
  "Produce map with column labels given raw data, return nil if not enough columns"
  [cols]
  (let [trimmed-cols (map str/trim cols)
        column-names {0 :fixture-type, 1 :division, 2 :home-team, 4 :away-team}]
    (if (> (count cols) (apply max (keys column-names)))
      (zipmap (vals column-names) (map trimmed-cols (keys column-names)))
      nil)))

(deftest test-mapping
  (let [cols '["L" "   Premier " "  Chelsea  " "v" "\tArsenal  "]
        fixture (column-map cols)]
    (is (= "Arsenal" (fixture :away-team)))
    (is (= "Chelsea" (fixture :home-team)))
    (is (= "Premier" (fixture :division)))
    (is (= "L" (fixture :fixture-type)))
  )
)

(run-tests 'fixtures.test.lazytest)

The approach I'm taking is:

  1. Clean up the vector of column data (remove leading/trailing spaces)
  2. Using zipmap, combine column name keywords with their corresponding element in the column data (note that not all columns are used)

The problem is, using the trimmed-cols in zipmap causes

java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.IFn

I think I know why this is happening ... as trimmed-cols is a LazySeq, the map called from zipmap is objecting to receiving a non-function as its first argument.

To fix this I can change the let to:

trimmed-cols (vec (map str/trim cols))

But this doesn't feel like the "best" option.

So:

  1. Is there a good general solution for using the result of a map operation as the "function" argument to another map?
  2. Is there a better approach for deriving a map of {: value} pairs from a vector of raw value data, where not all of the vector elements are used?

(I hesitate to ask for an idiomatic solution, but imagine somewhere there must be a generally accepted way of doing this.)

Was it helpful?

Solution

I am not entirely sure what you're after, but I can see why your code fails - like you said, trimmed-cols is not a function. Wouldn't this simply work?

I'm using :_dummy keys for columns that should be skipped. You can use as many as you like in the column-names vector: zipmap will merge them into one, which is removed by dissoc.

(defn- column-map
  "Produce map with column labels given raw data, return nil if not enough columns"
  [cols]
  (let [trimmed-cols (map str/trim cols)
        column-names [:fixture-type :division :home-team :_dummy :away-team]]
    (if (>= (count cols) (count column-names))
      (dissoc (zipmap column-names trimmed-cols) :_dummy)
      nil)))

(column-map ["L" "   Premier " "  Chelsea  " "v" "\tArsenal  "])
; => {:away-team "Arsenal", :home-team "Chelsea", :division "Premier", :fixture-type "L"}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top