Dynamically create vector based on the length of another vector - clojure

I'm trying to create a vector of keys to pass to assoc-in to describe where in a map the insert should take place. The second vector of an unknown size will dictate where I need to assoc-in based on its length.
For example if the length of the vector
is 1) I will need to assoc-in [:aggs :bucket-aggregation :aggs]
is 2) I will need to assoc-in [:aggs :bucket-aggregation :aggs :bucket-aggregation :aggs]
I have the following but can't get the vector to dynamically increase in size.
(vec (concat [:aggs] [:bucket-aggregation :aggs]))

according to my understanding of your question, i would go with something like this:
user> (defn make-query [based-on-v]
(vec
(apply concat [:aggs]
(repeat (count based-on-v) [:bucket-aggregation :aggs]))))
#'user/make-query
user> (make-query [1])
;;=> [:aggs :bucket-aggregation :aggs]
user> (make-query [1 2])
;;=> [:aggs :bucket-aggregation :aggs :bucket-aggregation :aggs]

Related

Calculating image of a Set in Clojure includes nil value?

(defn image-of
"computes the image of the element x under R"
[R x]
(set
(for [r R]
(when (= (first r) x)
(second r)))))
Function idea: Add the second variable in R when it's first is equal to x.
So this function is supposed to compute image of a relation. This is kinda successful. When running a test I get this result:
Input: (image-of #{[1 :a] [2 :b] [1 :c] [3 :a]} 1)
Expected: #{:c :a}
Actual: #{nil :c :a}
So it includes a nil value for some reason. What in the function causes this? I guess I could filter out any nil values but would like to have the solution on a single line.
So the problem was I didn't know exactly how to use when
This solution does it:
(set (for [r R
:when (= (first r) x)]
(second r)))
Let me suggest a different approach.
The natural way to represent a relation in Clojure is as a map from keys to sets (or other collections) of values. A function to convert your collection of pairs to this form is ...
(defn pairs->map [pairs]
(reduce
(fn [acc [key value]]
(assoc acc key (conj (acc key #{}) value)))
{}
pairs))
For example, ...
(pairs->map #{[1 :a] [2 :b] [1 :c] [3 :a]})
=> {2 #{:b}, 1 #{:c :a}, 3 #{:a}}
You can use this map as a function. I you feed it a key, it returns the corresponding value:
({2 #{:b}, 1 #{:c :a}, 3 #{:a}} 1)
=> #{:c :a}
You construct this map once and or all and use it as often as you like. Looking it up as a function is effectively a constant-time operation. But you run through the entire collection of pairs every time you evaluate image-of.

How to filter a sequence and retain evaluated pred values?

This is a scenario I encountered many times, yet didn't find an idiomatic approach for it...
Suppose one would like to use a self-defined self-pred function to filter a seq. This self-pred function returns nil for unwanted elements, and useful information for wanted elements. It is desirable to keep the evaluated self-pred values for these wanted elements.
My general solution is:
;; self-pred is a pred function which returns valuable info
;; in general, they are unique and can be used as key
(let [new-seq (filter self-pred aseq)]
(zipmap (map self-pred new-seq) new-seq))
Basically, it is to call self-pred twice on all wanted elements. I feel it is so ugly...
Wonder if there is any better ways. Much appreciated for any input!
In these scenarios you can use keep, but you have to change your "predicate" function to return the full information you need, or nil, for each item.
For example:
(keep (fn [item]
(when-let [tested (some-test item)]
(assoc item :test-output tested))) aseq)
i use this kind of snippet:
(keep #(some->> % self-pred (vector %)) data)
like this:
user> (keep #(some->> % rseq (vector %)) [[1 2] [] [3 4]])
;;=> ([[1 2] (2 1)] [[3 4] (4 3)])
or if you like more verbose result:
user> (keep #(some->> % rseq (hash-map :data % :result)) [[1 2] [] [3 4]])
;;=> ({:result (2 1), :data [1 2]} {:result (4 3), :data [3 4]})
I wouldn't bother with keep, but would just use plain map & filter like so:
(def data (range 6))
(def my-pred odd?)
(defn zip [& colls] (apply map vector colls)) ; like Python zip
(defn filter-with-pred
[vals pred]
(filter #(first %)
(zip (map pred vals) vals)))
(println (filter-with-pred data my-pred))
with result:
([true 1] [true 3] [true 5])
If self-pred guarantees no duplicate key creation for differing values then I'd reach for reduce (since assoc the same key twice will override the original key value pair):
(reduce #(if-let [k (self-pred %2)]
(assoc %1 k %2)
%1)
{}
aseq)
Else we can use group-by to drive a similar result:
(dissoc (group-by self-pred aseq) nil)
Although not the same since the values will be in vectors: {k1 [v1 ..], k2 [..], ..}. but this guarantees all values are kept.

How to apply max to a list of values with predefined order in Clojure?

I need to apply the max operator to the following list
[Tier20 Tier10 Tier30]
And it should give me
Tier30
The predefined ordered list (from low to high) is
[Tier5 Tier10 Tier20 Tier30 Tier40 Tier50]
What's the best way to achieve this in Clojure?
first define the ordering:
user> (def order '[Tier5 Tier10 Tier20 Tier30 Tier40 Tier50])
#'user/order
then we map the order onto something that can be sorted by creating a map
user> (def order-map (zipmap order (range)))
{Tier50 5, Tier40 4, Tier30 3, Tier20 2, Tier10 1, Tier5 0}
use the order-map to reduce the input if all you need is the max/min:
user> (reduce #(if (< (order-map %1) (order-map %2)) %1 %2)
'[Tier20 Tier10 Tier30])
Tier30
or if you need the full ordering then use the sort-by function, which is like the regular sort function except it gives you a chance to translate the input before comparison:
user> (sort-by (zipmap order (range)) '[Tier20 Tier10 Tier30])
(Tier10 Tier20 Tier30)
if you need to modify this map a lot and not re-sort it each time then use a sorted-set-by datastructure to store your inputs.
Use reduce. For instance, to find the maximum of a list L, you would do:
(reduce max L)
The real question here is if your Tiers are strings or more complex objects, as it isn't completely clear from your post. Either way, you can replace max with a lambda expression to pull out the numeric part of the string, like so:
(reduce (fn [x y] ...) L)
Where ... will be the expression that extracts the numeric portion out of your data.
Here's what I've just tried. The main point of leverage is the instance method .indexOf on clojure.lang.Vector.
user> (def x ['Tier5 'Tier10 'Tier20 'Tier30 'Tier40 'Tier50])
#'user/x
user> (index-of x 'Tier10)
; Evaluation aborted.
user> (.indexOf x 'Tier10)
1
user> (def y ['Tier20 'Tier10 'Tier30])
#'user/y
user> (reduce #(max %1 (.indexOf x %2)) y)
; Evaluation aborted.
user> (x (reduce #(max %1 (.indexOf x %2)) 0 y))
Tier30
user>
Naturally, this is O(n2). If you'll do this on a large scale, you'll be much better served by a hash-map from TierN to its ordering index.
If the array is of java.lang.String then with a bit of interop:
user=> (def array ["Tier5" "Tier10" "Tier20" "Tier30" "Tier40" "Tier50"])
#'user/array
user=> (defn find-max [m] (reduce #(if (> (.compareTo %1 %2) -1) %1 %2) m))
#'user/find-max
user=> (find-max array)
"Tier50"
user=> (find-max "a")
\a
If not just make them implement Comparable, or create a Comparator.

Clojure transients - assoc! causing exception

Here is the function I'm trying to run...
(defn mongean [cards times]
(let [_cards (transient cards)]
(loop [i 0 c (get cards i) _count (count cards) _current (/ _count 2)]
(assoc! _cards _current c)
(if ((rem i 2) = 0)
(def _newcur (- _current (inc i)))
(def _newcur (+ _current (inc i))))
(if (<= i _count)
(recur (inc i) (get cards i) _count _newcur )))
(persistent! _cards)))
It's resulting in this Exception...
Exception in thread "main" java.lang.ClassCastException: clojure.lang.PersistentHashSet$TransientHashSet cannot be cast to clojure.lang.ITransientAssociative
Being new to clojure, I'd also appreciate any constructive criticism of my approach above. The goal is to take a List, and return a re-ordered list.
I assume that you are trying to implement the Mongean shuffle. Your approach is very imperative and you should try to use a more functional approach.
This would be a possible implementation, were we calculate the final order of the cards (as per Wikipedia formula) and then we use the built-in replace function to do the mapping:
(defn mongean [cards]
(let [num-cards (count cards)
final-order (concat (reverse (range 1 num-cards 2)) (range 0 num-cards 2))]
(replace cards final-order)))
user> (mongean [1 2 3 4 5 6 7 8])
(8 6 4 2 1 3 5 7)
How do you call that function? It looks like you're passing a set, so that its transient version will also be a set and hence can't be used with any of the assoc functions, as they work on associative data structures and vectors:
user=> (assoc #{} :a 1)
ClassCastException clojure.lang.PersistentHashSet cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:691)
user=> (assoc! (transient #{}) :a 1)
ClassCastException clojure.lang.PersistentHashSet$TransientHashSet cannot be cast to clojure.lang.ITransientAssociative clojure.core/assoc! (core.clj:2959)
; the following works as it uses maps and vectors
user=> (assoc {} :a 1)
{:a 1}
user=> (assoc! (transient {}) :a 1)
#<TransientArrayMap clojure.lang.PersistentArrayMap$TransientArrayMap#65cd1dff>
user=> (assoc [] 0 :a)
[:a]
Now, let's try to discuss the code itself. It's a bit hard to follow your code and try to understand what the goal really is without some more hints on what you want to achieve, but as general comments:
you have a times input parameter you don't use at all
you are supposed to use the result of a transient mutation, not assume that the transient will mutate in place
avoid transients if you can, they're only meant as a performance optimization
the binding _current (/ _count 2) is probably not what you want, as (/ 5 2) really returns 5/2 and it seems that you want to use it as a position in the result
constants like _count don't need to be part of the loop binding, you can use the outer let so that you don't have to pass them at each and every iteration
use let instead of def for naming things inside a function
(if ((rem 1 2) = 0)) is definitely not what you want
Now, leaving aside the shuffling algorithm, if you need to rearrange a sequence you might just produce a sequence of new positions, map them with the original cards to produce pairs of [position card] and finally reduce them by placing the card at the new position, using the original sequence as the seed:
(defn generate [coll] ; counts down from (count coll) to 0, change to
; implement your shuffling algorithm
(range (dec (count coll)) -1 -1))
(defn mongean [cards times]
(let [positions (generate cards) ; get the new positions
assemble (fn [dest [pos card]] ; assoc the card at the wanted position
(assoc dest pos card))]
(reduce assemble cards (map vector positions cards))))
If you simply want to shuffle:
(defn mongean [cards times] (shuffle cards))

Clojure compress vector

I am trying to find a Clojure-idiomatic way to "compress" a vector:
(shift-nils-left [:a :b :c :a nil :d nil])
;=> (true [nil nil :a :b :c :a :d])
(shift-nils-left [nil :a])
;=> (false [nil :a])
(shift-nils-left [:a nil])
;=> (true [nil :a])
(shift-nils-left [:a :b])
;=> (false [:a :b])
In other words, I want to move all of the nil values to the left end of the vector, without changing the length. The boolean indicates whether any shifting occurred. The "outside" structure can be any seq, but the inside result should be a vector.
I suspect that the function will involve filter (on the nil values) and into to add to a vector of nils of the same length as the original, but I'm not sure how to reduce the result back to the original length. I know how to this "long-hand", but I suspect that Clojure will be able to do it in a single line.
I am toying with the idea of writing a Bejeweled player as an exercise to learn Clojure.
Thanks.
I would write it like this:
(ns ...
(:require [clojure.contrib.seq-utils :as seq-utils]))
(defn compress-vec
"Returns a list containing a boolean value indicating whether the
vector was changed, and a vector with all the nils in the given
vector shifted to the beginning."
([v]
(let [shifted (vec (apply concat (seq-utils/separate nil? v)))]
(list (not= v shifted)
shifted))))
Edit: so, the same as what Thomas beat me to posting, but I wouldn't use flatten just in case you end up using some sort of seqable object to represent the jewels.
Maybe this way:
(defn shift-nils-left
"separate nil values"
[s]
(let [s1 (vec (flatten (clojure.contrib.seq/separate nil? s)))]
(list (not (= s s1)) s1)))
A little more low-level approach. It traverses the input seq just once as well as the vector of non-nils once. The two more highlevel approaches traverse the input sequence two times (for nil? and (complenent nil?)). The not= traverses the input a third time in the worst-case of no shift.
(defn compress-vec
[v]
(let [[shift? nils non-nils]
(reduce (fn [[shift? nils non-nils] x]
(if (nil? x)
[(pos? (count non-nils)) (conj nils nil) non-nils]
[shift? nils (conj non-nils x)]))
[false [] []] v)]
[shift? (into nils non-nils)]))
(def v [1 2 nil 4 5 nil 7 8] )
(apply vector (take 8 (concat (filter identity v) (repeat nil))))
This creates a sequence of the non- nil values in the vector using filter and then appends nils to the end of the sequence. This gives the values you want as a sequence and then converts them into a vector. The take 8 ensures that the vector is right size.