clojurescript thread macro nested vector problem - functional-programming

this is my function
(defn foo
[]
(->> (conj (:countries list1) ;; ["UK" "USA" "IND"]
(:countries list2) ;; ["AUS" "NZ"]
(:countries list3) ;; "FRA"
)
(some-other-operations)))
comment shows the data they fetches and I'm expecting my result to be like this
["UK" "USA" "IND" "AUS" "NZ" "FRA"]
but i don't why it gives me output like this
["UK" "USA" ["AUS" "NZ"] "FRA"]
it works fine though if i remove list2. then it produces output like this
["UK" "USA" "FRA"]
anyone any idea how can i fix this?

the root problem, as i see it, is an irregular :countries value type, being collection or atom in different cases. What i would propose, is ensure the :contries is always a collection and use concat. In case you don't have control over the data type (which often happens), i'd propose utility concatenation function, something like that:
(defn concat* [& xs]
(reduce (fn [acc x]
((if (sequential? x) into conj) acc x))
[] xs))
user> (concat* ["UK" "USA" "IND"] ["AUS" "NZ"] "FRA")
;;=> ["UK" "USA" "IND" "AUS" "NZ" "FRA"]
as for me, i would discourage using flatten in production code, since it may lead to the whole bunch of errors when data type changes.

First two values are lists, but the third value is not a list (FRA). If it were, you could use concat.
(concat list1 list2 list3)
If not, then
(let [list1 ["UK" "USA" "IND"]
list2 ["AUS" "NZ"]
v3 "FRA"]
`(~#list1 ~#list2 ~v3))

You can flatten the result to "unpack" the nested vector that is now inside the collection.

Related

In Dr. Racket, how would I convert a list of strings to a list of list of strings

In Dr. Racket, how would I convert a list containing strings (each string is separated by a space) to a list containing a list of the separated strings. If you can't understand the question too well, here is an example:
Given a list like:
(list "abc def" "ghj elm" "ref tio" "pol mnb")
I want to produce:
(list (list "abc" "def") (list "ghj" "elm") (list "ref" "tio") (list "pol" "mnb"))
I wrote up a code for it but I quickly got stuck. Here is my code so far:
(define (a los)
(cond
[(empty? los) empty]
[(char-whitespace? (first los)) (substring (list->string los) 1)]
[else (a (rest los))]))
For example:
(a (string->list "abc def"))
Produces "def".
I was hoping to first separate the strings in the list based on the whitespace and then I would piece them together to form new lists. However, I don't know how to do this.
Any tips/hints on how to do this are much appreciated.
There's a built-in procedure called string-split that does just what you need, it's as simple as this:
(define (split-list los)
(map string-split los))
It works as expected:
(split-list '("abc def" "ghj elm" "ref tio" "pol mnb"))
=> '(("abc" "def") ("ghj" "elm") ("ref" "tio") ("pol" "mnb"))

Clojure: Vector is not immutable

I'm running into a problem where immutability suddenly doesn't hold for my vectors. I was wondering if there was a way to create fresh, immutable vector copies of a given set.
Clojuredocs suggested "aclone" but that is giving me an error stating that there's no such method.
(defn stripSame [word wordList]
(def setVec (into #{} wordList))
(def wordSet word)
(def wordVec (into #{} [wordSet]))
(def diffSet (set/difference setVec wordVec))
(def diffVec (into [] diffSet))
diffVec)
(defn findInsOuts [word passList]
(def wordList (stripSame word passList))
(println word wordList)
(def endLetter (subs word (dec (count word))))
(def startLetter (subs word 0 1))
(println startLetter endLetter)
(def outs (filter (partial starts endLetter) wordList))
(def ins (filter (partial ends startLetter) wordList))
;(println ins outs)
(def indexes [ (count ins) (count outs)])
indexes)
(defn findAll [passList]
(def wordList (into [] passList) ))
(println wordList)
(loop [n 0 indexList []]
(println "In here" (get wordList n) wordList)
(if (< n (count wordList))
(do
(def testList wordList)
(def indexes (findInsOuts (get wordList n) testList))
(println (get wordList n) indexes)
(recur (inc n) (conj indexList [(get wordList n) indexes]))))))
passList is a list of words like so (lol at good) which is then cast into a vector.
So basically findAll calls findInsOuts which goes through every word in the list and sees how many other words start with its last letter but which first removes the search word from the vector before performing some function to prevent duplicates. The problem is that somehow this vector is actually mutable, so the copy of the vector in findAll also has that value permanently stripped.
When I try to create a new vector and then act on that vector the same thing still happens, which implies that they're aliased/sharing the same memory location.
How can I create a fresh vector for use that is actually immutable?
Any help is appreciated
I'm afraid your code is riddled with misunderstandings. For a start, don't use def within defn. Use let instead. This turns your first function into ...
(defn stripSame [word wordList]
(let [setVec (into #{} wordList)
wordSet word
wordVec (into #{} [wordSet])
diffSet (clojure.set/difference setVec wordVec)
diffVec (into [] diffSet)]
diffVec))
For example,
=> (stripSame 2 [1 2 :buckle-my-shoe])
[1 :buckle-my-shoe]
The function can be simplified to ...
(defn stripSame [word wordList]
(vec (disj (set wordList) word)))
... or, using a threading macro, to ...
(defn stripSame [word wordList]
(-> wordList set (disj word) vec))
I don't think the function does what you think it does, because it doesn't always preserve the order of elements in the vector.
If I were you, I'd work my way through some of the community tutorials on this page. There are several good books referred to there too. Once you get to grips with the idioms of the language, you'll find the sort of thing you are trying to do here much clearer and easier.

Clojure: summing values in a collection of maps until a value is reached.

My goal is to add up values in a collection of maps until a value is reached in one of the map values. I tried figuring it out using this example but it didn't touch on how I could only grab part of the list. Then return the collection without the values that went over. Something like this
(def foo '({:key 1, :value 2} {:key 1, :value 2} {:key 1, :value 2})
(defn addValuesUp [foo]
(take-while (< ((apply merge-with + foo) :value) 4) foo))
and have it return something like this
'({:key 1, :value 2} {:key 1, :value 2})
Instead I get an error Boolean cannot be cast to clojure.lang.IFn
The way I will solve the problem will be by adding to the maps a new key with the accumulation of all the previous values so you can do a simple take-while:
(defn sums-of-values [c]
(reductions + c))
(defn with-accum [c]
(map #(assoc %1 :accum %2)
c
(sums-of-values (map :value c))))
Now that the maps have a :accum slot, you can use take-while:
(take-while (comp (partial >= 4)
:accum)
(with-accum foo))
Have a look at the documentation for take-while:
clojure.core/take-while
([pred coll])
Returns a lazy sequence of successive items from coll while
(pred item) returns true. pred must be free of side-effects.
pred in this case is a function that returns a boolean. In your code, your first argument to take-while is not a function but an expression instead.
This is why you get the error ClassCastException java.lang.Boolean cannot be cast to clojure.lang.IFn. This error is telling you that where Clojure expects a function (IFn) but it found a boolean (the result of your expression).
Once you turn that into a function, you should make progress. Some more work might be required in the function implementation however.

clojure reduce a seq of maps to a map of vectors

I would like to reduce the following seq:
({0 "Billie Verpooten"}
{1 "10:00"}
{2 "17:00"}
{11 "11:10"}
{12 "19:20"})
to
{:name "Billie Verpooten"
:work {:1 ["10:00" "17:00"]
:11 ["11:10" "19:20"]}}
but I have no idea to do this.
I was think about a recursive function that uses deconstruction.
There's a function for reducing a sequence to something in the standard library, and it's called reduce. Though in your specific case, it seems appropriate to remove the special case key 0 first and partition the rest into the pairs of entries that they're meant to be.
The following function gives the result described in your question:
(defn build-map [maps]
(let [entries (map first maps)
key-zero? (comp zero? key)]
{:name (val (first (filter key-zero? entries)))
:work (reduce (fn [acc [[k1 v1] [k2 v2]]]
(assoc acc (keyword (str k1)) [v1 v2]))
{}
(partition 2 (remove key-zero? entries)))}))
Just for variety here is a different way of expressing an answer by threading sequence manipulation functions:
user> (def data '({0 "Billie Verpooten"}
{1 "10:00"}
{2 "17:00"}
{11 "11:10"}
{12 "19:20"}))
user> {:name (-> data first first val)
:work (as-> data x
(rest x)
(into {} x)
(zipmap (map first (partition 1 2 (keys x)))
(partition 2 (vals x))))}
teh as-> threading macro is new to Clojure 1.5 and makes expressing this sort of function a bit more concise.

Clojure: How do I apply a function to a subset of the entries in a hash-map?

I am not to Clojure and attempting to figure out how to do this.
I want to create a new hash-map that for a subset of the keys in the hash-map applies a function to the elements. What is the best way to do this?
(let
[my-map {:hello "World" :try "This" :foo "bar"}]
(println (doToMap my-map [:hello :foo] (fn [k] (.toUpperCase k)))
This should then result a map with something like
{:hello "WORLD" :try "This" :foo "BAR"}
(defn do-to-map [amap keyseq f]
(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq))
Breakdown:
It helps to look at it inside-out. In Clojure, hash-maps act like functions; if you call them like a function with a key as an argument, the value associated with that key is returned. So given a single key, the current value for that key can be obtained via:
(some-map some-key)
We want to take old values, and change them to new values by calling some function f on them. So given a single key, the new value will be:
(f (some-map some-key))
We want to associate this new value with this key in our hash-map, "replacing" the old value. This is what assoc does:
(assoc some-map some-key (f (some-map some-key)))
("Replace" is in scare-quotes because we're not mutating a single hash-map object; we're returning new, immutable, altered hash-map objects each time we call assoc. This is still fast and efficient in Clojure because hash-maps are persistent and share structure when you assoc them.)
We need to repeatedly assoc new values onto our map, one key at a time. So we need some kind of looping construct. What we want is to start with our original hash-map and a single key, and then "update" the value for that key. Then we take that new hash-map and the next key, and "update" the value for that next key. And we repeat this for every key, one at a time, and finally return the hash-map we've "accumulated". This is what reduce does.
The first argument to reduce is a function that takes two arguments: an "accumulator" value, which is the value we keep "updating" over and over; and a single argument used in one iteration to do some of the accumulating.
The second argument to reduce is the initial value passed as the first argument to this fn.
The third argument to reduce is a collection of arguments to be passed as the second argument to this fn, one at a time.
So:
(reduce fn-to-update-values-in-our-map
initial-value-of-our-map
collection-of-keys)
fn-to-update-values-in-our-map is just the assoc statement from above, wrapped in an anonymous function:
(fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
So plugging it into reduce:
(reduce (fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
amap
keyseq)
In Clojure, there's a shorthand for writing anonymous functions: #(...) is an anonymous fn consisting of a single form, in which %1 is bound to the first argument to the anonymous function, %2 to the second, etc. So our fn from above can be written equivalently as:
#(assoc %1 %2 (f (%1 %2)))
This gives us:
(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq)
(defn doto-map [m ks f & args]
(reduce #(apply update-in %1 [%2] f args) m ks))
Example call
user=> (doto-map {:a 1 :b 2 :c 3} [:a :c] + 2)
{:a 3, :b 2, :c 5}
Hopes this helps.
The following seems to work:
(defn doto-map [ks f amap]
(into amap
(map (fn [[k v]] [k (f v)])
(filter (fn [[k v]] (ks k)) amap))))
user=> (doto-map #{:hello :foo} (fn [k] (.toUpperCase k)) {:hello "World" :try "This" :foo "bar"})
{:hello "WORLD", :try "This", :foo "BAR"}
There might be a better way to do this. Perhaps someone can come up with a nice one-liner :)

Resources