3

Apparently get-in doesn't work for '() lists since they're not an associative data structure. This makes sense for the API and from the perspective of performance of large lists. From my perspective as a user it'd be great to still use this function to explore some small test data in the repl. For example I want to be able to:

(-> '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
     (get-in [1 :a 0]))

=> "one"

Is there some other function that works this way? Is there some other way to achieve this behavior that doesn't involve converting all my lists to (say) vectors?

user12341234
  • 6,573
  • 6
  • 23
  • 48
  • Incidentally, SO wouldn't let me post this question until I upper-cased the G in `get-in` in the title. That seems like a bad feature... – user12341234 Apr 04 '17 at 17:12

3 Answers3

3

This does what you ask:

(defn get-nth-in [init ks]
  (reduce
    (fn [a k]
      (if (associative? a)
        (get a k)
        (nth a k)))
    init
    ks))

For example,

(-> '({:a "zero"} {:a "one"} {:a "two"})
     (get-nth-in [1 :a]))
;"one"

and

(-> '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
     (get-nth-in [1 :a 0]))
;"one"

The extra 's you have get expanded into (quote ...):

(-> '({:a '("zero" 0)} {:a '("one" 1)} {:a '("two" 2)})
     (get-nth-in [1 :a 0]))
;quote

Not what you intended, I think.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37
3

A post just yesterday had a problem regarding lazy lists and lazy maps (from clojure/data.xml). One answer was to just replace the lazy bits with plain vectors & maps using this function:

(defn unlazy
  [coll]
  (let [unlazy-item (fn [item]
                      (cond
                        (sequential? item) (vec item)
                        (map? item) (into {} item)
                        :else item))
        result    (postwalk unlazy-item coll)
  ]
    result ))

Since the resulting data structure uses only vectors & maps, it works for your example with get-in:

 (let [l2  '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
       e2  (unlazy l2) ]
      (is= l2 e2)
      (is= "one" (get-in e2 [1 :a 0] l2))
    )

You can find the unlazy function in the Tupelo library.

Community
  • 1
  • 1
Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • In my particular case unlazying is quite time consuming, so for me the accepted answer is still preferable, but this is still a great function to keep around for a rainy day. – user12341234 Apr 04 '17 at 19:30
-1

The first param for get-in should be a map.

You have to figure out the feature of your sequence, use last, first, filter or some e.g. to get the element first

for example you could use (:a (last data))

tony
  • 67
  • 4
  • The first param doesn't need to be map, it can be a vector, for example. I updated my question to break your example solution. – user12341234 Apr 04 '17 at 17:31
  • That is why I said you have to figure out the feature of your sequence if you are willing to use index , do it like this `(-> (vec '({:a "zero"} {:a "one"})) (get-in [1 :a]))` However ,using index in vector is not a perfect method – tony Apr 04 '17 at 17:41