THX1137
THX1137

Reputation: 973

how to retrieve a string that is in the first position of a list that is a hash-map value

If I evaluate:

(:content {:foo "bar" :biz "baf" :content ("Happy Happy Joy Joy")})

I get:

java.lang.String cannot be cast to clojure.lang.IFn

If I wanted the "Happy Happy Joy Joy" string, how do I get it?

In my case, the hash-map is what I have to work with... I didn't create the string value inside a list. I understand clojure considers it a function as it's in the calling position.

Upvotes: 1

Views: 84

Answers (3)

Alan Thompson
Alan Thompson

Reputation: 29958

The other answers did not fully clarify the effect of quote. Please see this code:

(ns tst.demo.core
  (:use tupelo.test)
  (:require
    [tupelo.core :as t] ))

; Note:
; (def data {:foo "bar" :biz "baf" :content ("Happy Happy Joy Joy")})
;   => exception

(def data-1       '{:foo "bar" :biz "baf" :content  ("Happy Happy Joy Joy")})
(def data-2        {:foo "bar" :biz "baf" :content '("Happy Happy Joy Joy")})
(def data-3 (quote {:foo "bar" :biz "baf" :content  ("Happy Happy Joy Joy")}))
(dotest
  (is= data-1 data-2 data-3)
  (is= "Happy Happy Joy Joy" (first (:content data-1)))
  (is= "Happy Happy Joy Joy" (first (:content data-2)))
  (is= "Happy Happy Joy Joy" (first (:content data-3))))

So, data-1 shows we can quote the entire expression at the outer level, and data-2 shows we can also quote each list expression (stuff in parens) to suppress the "function call" interpretation of a "list" type in Clojure.

data-3 shows that the single-quote char ' is just short for the special form (quote ...) in Clojure.

Once you get the data literal form right, we see that data-1 and data-2 and data-3 actually result in identical data structures after being processed by the reader.

The last 3 tests show the proper syntax for extracting the string of interest from any of the 3 data structures.


P.S. The testing stuff dotest and is= is from the Tupelo library.

Upvotes: 0

Chris Murphy
Chris Murphy

Reputation: 6509

What is typed that has to include quote (') literals to prevent the error message you are getting will be different from what is being returned from a function that does not have to have quotes in it. So just play with it a bit for the real (non REPL) case.

(def x '(:content {:foo "bar" :biz "baf" :content '("Happy Happy Joy Joy")}))

(-> x second :content second first)
;;=> "Happy Happy Joy Joy"

For the real case (-> x second :content first) might be what you want, where of course x is the function call.

If as you say it is only the hash-map (m) you are concerned with then (-> m :content first) should do the trick.

One solution to the mismatch between the REPL and reality is to just use vectors instead of lists:

(def x [:content {:foo "bar" :biz "baf" :content ["Happy Happy Joy Joy"]}])

Here (-> x second :content first) will indeed work.

Upvotes: 1

Taylor Wood
Taylor Wood

Reputation: 16194

If you're defining that list literally in your code, you'll need to "quote" it so that it isn't evaluated as a function:

user=> (:content {:foo "bar" :biz "baf" :content '("Happy Happy Joy Joy")})
("Happy Happy Joy Joy")

The only difference here is the ' character before the opening list parenthesis. You could also use the list function.

If you want just the first item in the :content list, you can then use first:

user=> (first (:content {:foo "bar" :biz "baf" :content '("Happy Happy Joy Joy")}))
"Happy Happy Joy Joy"

Upvotes: 1

Related Questions