get-in for lists - clojure

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?

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.

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.

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))

Related

Clojure - Create an array of keys if value is true

I am totally new to clojure.
I have a JSON like: { "1": true, "2": false, "3": true, "4": false }
I want to create an array of keys for which the value is true in clojure. In this example the array should be ["1", "3"].
Please help me. Any help would be appreciated.
there are also couple of short and simple snippets for that:
user> (filter m (keys m))
;;=> ("1" "3")
user> (keep (fn [[k v]] (when v k)) m)
;;=> ("1" "3")
user> (for [[k v] m :when v] k)
;;=> ("1" "3")
If you're fine with using a vector instead of an array (since you're usually using vectors in Clojure anyway), you can do something like.
(defn keys-for-truthy-vals [m]
(->> m (filter val) (mapv key)))
Note The mapv is only so the map call returns a vector. If you want a seq, just use map.
The same as already provided, just staying in maps.
(keys (filter val m))
If your map is a Something like (->> (filter (fn [[k v]] v) a) (map (fn [[k v]] k))) will work. You can't do it with just a map because you need to drop certain values, so there will need to be some reducing or filtering.
There is built-in function in the Tupelo library for this:
(submap-by-vals map-arg keep-vals & opts)
Returns a new map containing entries with the specified vals. Throws for missing vals,
unless `:missing-ok` is specified. Usage:
(submap-by-vals {:a 1 :b 2 :A 1} #{1 } ) => {:a 1 :A 1}
(submap-by-vals {:a 1 :b 2 :A 1} #{1 9} :missing-ok ) => {:a 1 :A 1}
You could then just use the keys function on the resulting map.
Maybe this?
(->> foo (filter second) keys)
where foo is a map.

into vs. partition

This makes sense:
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}
But why does this generate an error?
user=> (into {} (partition 2 [:a 1 :b 2]))
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44)
Just to be sure:
user=> (partition 2 [:a 1 :b 2])
((:a 1) (:b 2))
Does into have a problem with lazy sequences? If so, why?
Beyond an explanation of why this doesn't work, what is the recommended way to conj a sequence of key-value pairs like [:a 1 :b 2] into a map? (apply conj doesn't seem to work, either.)
You can apply the sequence to assoc:
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}
Does into have a problem with lazy sequences? If so, why?
No, into is commonly used with lazily evaluated sequences. This is lazy, but each key/value tuple is a vector, which is why it works when into is reducing the pairs into the map:
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}
This doesn't work because the key/value pairs are lists:
(into {} (map list (range 3) (repeat :x)))
So the difference isn't laziness; it's due to into using reduce using conj on the map, which only works with vector key/value pairs (or MapEntrys):
(conj {} [:a 1]) ;; ok
(conj {} (MapEntry. :a 1)) ;; ok
(conj {} '(:a 1)) ;; not ok
Update: assoc wrapper for applying empty/nil sequences as suggested in comments:
(defn assoc*
([m] m)
([m k v & kvs]
(apply assoc m k v kvs)))
The recommended way – (assuming the seq arg is non-empty, as pointed out by the OP) – would be
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}
The version with partition doesn't work because the blocks that partition returns are seqs and those are not treated as map entries when conj'd on to a map the way vectors and actual map entries are.
E.g. (into {} (map vec) (partition 2 [:a 1 :b 2])) would work because here the pairs get converted to vectors before conjing.
Still the approach with assoc is preferable unless there's some particular circumstance that makes into convenient (like, say, if you have a bunch of transducers that you want to use for preprocessing your partition-generated pairs etc.).
Clojure treats a 2-vec such as [:a 1] as equivalent to a MapEntry, doing what amounts to "automatic type conversion". I try to avoid this and always be explicit.
(first {:a 1}) => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2]) => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
So we see that a MapEntry prints like a vector but has a different type (just like a Clojure seq prints like a list but has a different type). seq converts a Clojure map into a sequence of MapEntry's, and first gets us the first one (most Clojure functions call (seq ...) on any input collections before any other processing).
Notice that conj does the inverse type conversion, treating the vector [:b 2] as if it were a MapEntry. However, conj won't perform automatic type conversion for a list or a seq:
(throws? (conj {:a 1} '(:b 2)))
(throws? (into {:a 1} '(:b 2)))
into has the same problem since it is basically just (reduce conj <1st-arg> <2nd-seq>).
The other answers already have 3 ways that work:
(assoc {} :b 2) => {:b 2}
(conj {} [:b 2]) => {:b 2}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
However, I would avoid those and stick to either hash-map or sorted-map, both of which avoid the problem of empty input seqs:
(apply hash-map []) => {} ; works for empty input seq
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
If your input sequence is a list of pairs, flatten is sometimes helpful:
(apply sorted-map (flatten [[:a 1] [:b 2]])) => {:a 1, :b 2}
(apply hash-map (flatten '((:a 1) (:b 2)))) => {:a 1, :b 2}
P.S.
Please be note that these are not the same:
java.util.Map$Entry (listed in jdk docs as "Map.Entry")
clojure.lang.MapEntry
P.P.S
If you already have a map and want to merge in a (possibly empty) sequence of key-value pairs, just use a combination of into and hash-map:
(into {:a 1} (apply hash-map [])) => {:a 1}
(into {:a 1} (apply hash-map [:b 2])) => {:a 1, :b 2}

how can you interleave two vectors of differing lengths in clojure

What is the simplest way to interleave two vectors with n+1 and n members?
(def a [:a :c :e])
(def b [:b :d])
(interleave a b ); truncates to shortest list
[:a :b :c :d]
;what I would like.
(interleave-until-nil a b)
[:a :b :c :d :e]
Cons the first, interleave the rest with arguments reversed.
(cons (first a) (interleave b (rest a)))
;=> (:a :b :c :d :e)
Conj nil to the second, interleave colls get all butlast
(butlast (interleave a (conj b nil)))
;=> (:a :b :c :d :e)
(defn interleave+ [& x]
(take (* (count x) (apply max (map count x)))
(apply interleave (map cycle x))))
(butlast (interleave+ [:a :c :e] [:b :d]))
=> (:a :b :c :d :e)
Tried this as an exercise in lazy seqs. I suspect that there are more elegant ways though.
(defn interleave-all
"interleaves including remainder of longer seqs."
[& seqs]
(if (not-empty (first seqs))
(cons (first (first seqs)) (lazy-seq (apply interleave-all (filter not-empty (concat (rest seqs) [(rest (first seqs))])))))))
If you would like to have nil appended to always have same dimension results, this could be a way to do that:
(defn interleave-all [& seqs]
(reduce
(fn [a i]
(into a (map #(get % i) seqs)))
[]
(range (apply max (map count seqs)))))
For example:
(interleave-all [:a] [:b :c])
outputs:
[:a :b nil :c]
This can be used to transpose a matrix:
(defn matrix-transpose [input]
(partition
(count input)
(apply interleave-all input)))
Example:
(matrix-transpose [[:a] [:b :c]])
Outputs:
[[:a :b] [nil :c]]
Which can be used to i.e. tabular output of lists of differing lengths (but where you need the fixed dimensions to insert nothing where lists have no value for certain indices).

All subsets of a set in clojure

I wish to generate all subsets of a set except empty set
ie
(all-subsets #{1 2 3}) => #{#{1},#{2},#{3},#{1,2},#{2,3},#{3,1},#{1,2,3}}
How can this be done in clojure?
In your :dependencies in project.clj:
[org.clojure/math.combinatorics "0.0.7"]
At the REPL:
(require '[clojure.math.combinatorics :as combinatorics])
(->> #{1 2 3}
(combinatorics/subsets)
(remove empty?)
(map set)
(set))
;= #{#{1} #{2} #{3} #{1 2} #{1 3} #{2 3} #{1 2 3}}
clojure.math.combinatorics/subsets sensibly returns a seq of seqs, hence the extra transformations to match your desired output.
Here's a concise, tail-recursive version with dependencies only on clojure.core.
(defn power [s]
(loop [[f & r] (seq s) p '(())]
(if f (recur r (concat p (map (partial cons f) p)))
p)))
If you want the results in a set of sets, use the following.
(defn power-set [s] (set (map set (power s))))
#zcaudate: For completeness, here is a recursive implementation:
(defn subsets
[s]
(if (empty? s)
#{#{}}
(let [ts (subsets (rest s))]
(->> ts
(map #(conj % (first s)))
(clojure.set/union ts)))))
;; (subsets #{1 2 3})
;; => #{#{} #{1} #{2} #{3} #{1 2} #{1 3} #{2 3} #{1 2 3}} (which is correct).
This is a slight variation of #Brent M. Spell's solution in order to seek enlightenment on performance consideration in idiomatic Clojure.
I just wonder if having the construction of the subset in the loop instead of another iteration through (map set ...) would save some overhead, especially, when the set is very large?
(defn power [s]
(set (loop [[f & r] (seq s) p '(#{})]
(if f (recur r (concat p (map #(conj % f) p)))
p))))
(power [1 2 3])
;; => #{#{} #{3} #{2} #{1} #{1 3 2} #{1 3} #{1 2} #{3 2}}
It seems to me loop and recuris not lazy.
It would be nice to have a lazy evaluation version like Brent's, to keep the expression elegancy, while using laziness to achieve efficiency at the sametime.
This version as a framework has another advantage to easily support pruning of candidates for subsets, when there are too many subsets to compute. One can add the logic of pruning at position of conj. I used it to implement the prior algorithm for "Frequent Item Set".
refer to: Algorithm to return all combinations of k elements from n
(defn comb [k l]
(if (= 1 k) (map vector l)
(apply concat
(map-indexed
#(map (fn [x] (conj x %2))
(comb (dec k) (drop (inc %1) l)))
l))))
(defn all-subsets [s]
(apply concat
(for [x (range 1 (inc (count s)))]
(map #(into #{} %) (comb x s)))))
; (all-subsets #{1 2 3})
; (#{1} #{2} #{3} #{1 2} #{1 3} #{2 3} #{1 2 3})
This version is loosely modeled after the ES5 version on Rosetta Code. I know this question seems reasonably solved already... but here you go, anyways.
(fn [s]
(reduce
(fn [a b] (clojure.set/union a
(set (map (fn [y] (clojure.set/union #{b} y)) a))))
#{#{}} s))

What is idiomatic Clojure to "remove" a single instance from many in a list?

I have a list, which may contain elements that will compare as equal. I would like a similar list, but with one element removed. So from (:a :b :c :b :d) I would like to be able to "remove" just one :b to get (:a :c :b :d).
The context is a hand in a card game where two decks of standard cards are in play, so there may be duplicate cards but still played one at a time.
I have working code, see below. Are there more idiomatic ways to do this in Clojure?
(defn remove-one [c left right]
(if (= right ())
left
(if (= c (first right))
(concat (reverse left) (rest right))
(remove-one c (cons (first right) left) (rest right)))))
(defn remove-card [c cards]
(remove-one c () cards))
Here are the Scala answers I got a while ago: What is an idiomatic Scala way to "remove" one element from an immutable List?
How about:
(let [[n m] (split-with (partial not= :b) [:a :b :c :b :d])] (concat n (rest m)))
Which splits the list at :b and then removes the :b and concats the two lists.
I usually solve these problems with a higher-order function like split-with, but someone's already done that. Sometimes it's more readable or more efficient to work at a more primitive level, so here's a better version of your original looping code, using lazy sequences and generalized to take a predicate for removal instead of being constrained to equality checks:
(defn remove-once [pred coll]
((fn inner [coll]
(lazy-seq
(when-let [[x & xs] (seq coll)]
(if (pred x)
xs
(cons x (inner xs))))))
coll))
user> (remove-once #{:b} [:a :b :c :b :d])
(:a :c :b :d)
It is surprising there is not a high-level API to do something like this. Here is another version similar to #amalloy and #James that uses recur in order not to stack overflow.
(defn remove-once [x c]
(letfn [(rmv [x c1 c2 b]
(if-let [[v & c] (seq c1)]
(if (and (= x v) b)
(recur x c c2 false)
(recur x c (cons v c2) b))
c2))]
(lazy-seq (reverse (rmv x c '() true)))))
(remove-once :b [:a :b :c :b :d])
;; (:a :c :b :d)