Failed to update a map with assoc in Clojure [duplicate] - clojure

This question already has answers here:
Why does (assoc-in everything ...) not change everything?
(2 answers)
Closed 2 months ago.
I have following map in my Clojure code:
typeList {"int" {"type" ["integer"]
"minimum" -2147483648
"maximum" 2147483647}
"bigint" {"type" ["integer"]
"minimum" -9223372036854775808
"maximum" 9223372036854775807}}
I am trying to add some new values to that map and I am using assoc key for that; however it seems it is not adding the new value since the println is not giving the new keyword.
For example, let's add "asd" to the map:
(assoc typeList "asd" {"type" ["integer"]})
However, when I try to print the new list, it returns as following:
(println typeList)
{int {type [integer], minimum -2147483648, maximum 2147483647}, bigint
{type [integer], minimum -9223372036854775808, maximum
9223372036854775807}}
Am I missing something? Couldn't figure that out since I am newbie in Clojure.

assoc will not change the existing value typeList, instead it returns a new value with the key "asd". In other words, typeList will be unchanged and the return value from assoc will contain the "asd" key.
Try this instead:
(def updatedTypeList (assoc typeList "asd" {"type" ["integer"]}))
(get typeList "asd")
;; => nil
(get updatedTypeList "asd")
;; => {"type" ["integer"]}

Related

From list of pairs ((3 . #\J) (5 . #\Q)) to list of strings (("3J") (5Q)) in Scheme

I have a list of pairs ((3. #\K) (5 . #\J)) ... and I would like to create a function in scheme that returns the list as this: ("3K", "5J")...
I've beeing trying but I cannot make it.
This is what I have.
; The deckCards will contain the list of pairs;
; The real Deck will contain the empty list.
(define (deck->strings deckCards realDeck)
(let lenOfItem ([n (my-lenght deckCards)])
(if (= 1 n)
(list (card->string (first deckCards)))
(append realDeck (deck->strings (cdr deckCards) realDeck))))
)
I did try doing with cond but for some reason it doesnt return the list and it seems impossible to append the list to the realDeack before calling itself recursively.
I think I found an approach and it worked. Not sure if it good to use it. However, this prints all the strings from top to boottom in a new line... Will this matter? I think its because I have 48 elements.
(map (lambda (i) (card->string i))
clubs)

How to use schema.core/enum in clojure?

Given an enum made from prismatic schema.core/enum, let's say:
(def myenumtype (schema.core/enum "a" "b" "c"))
How can I set another def to a specific enum item? Here I would like to set e to the "a" enum item.
(def e (??? myenumtype))
And how can I compare this to a specific enum? Here I would like to check to see if e is equal to the "a" enum type.
(= e ((??? "a") myenumtype))
I think you're misunderstanding how Schema works. You're not creating an enum type, you're creating a validator that checks if a particular value equals one of the enumerated values.
In your case, all you need to do is:
(def e "a")
Here's an example REPL session:
user=> (schema.core/validate (schema.core/enum "a" "b" "c") "a")
"a"
=> (schema.core/validate (schema.core/enum "a" "b" "c") "z")
clojure.lang.ExceptionInfo: Value does not match schema: (not (#{"a" "b" "c"} "z"))

Convert nested vector maps in clojure

I need to collect and transfer :c/name values into nested vector to first level same way.
Input example:
[:a/name "name" :a/vals [{:b/val [{:c/name "one"}{:c/name "two"}]}
{:b/val [{:c/name "three"}]}]]
Output:
[:a/name :a/vals "one, two, three"]
This produces the output from the input, is this what you want?
(defn f [[k1 _ k2 rels]]
[k1 k2
(clojure.string/join ", "
(map :c/name (apply concat (mapcat vals rels))))])

How to check whether a value is fall into a range or not in clojure

The range could be defined by maxInclude, maxExclude,minInclude,minExclude
(defn check-range [value maxInclude maxExclude minInclude minExclude] ...)
And the following should hold true.
(check-range 100 100 nil nil 10) ;; should return true since 100<=100 and 100>10
(check-range 100 nil 200 nil 10) ;; should return true since 100<200 and 100>10
(check-range 100 100 nil 101 nil) ;; should return false since 100<=101 is not true
Is there any simple solution? I am using a long code which looks like imperative solution. I think in clojure there must be some nice solutions.
update: my code is as below, but not complete and need help to complete it
(defn check-range [value maxInclude maxExclude minInclude minExclude]
(
let [value1 (if (and maxInclude (< value maxInclude)) false true)
value2 (if (and maxExclude (<= value maxExclude)) false true)
value3 (if (and minInclude (> value minInclude)) false true)
value4 (if (and minExclude (>= value minExclude)) false true)
]
;;; then how to combine value1,2,3,4 into final result as false or true.
)
)
)
I'm not sure what it means for a range to have both an inclusive and exclusive maximum (or similarly, minimum). It seems like those options should be mutually exclusive, which suggests you shouldn't let clients opt into choosing both. Otherwise, how do you decide if it's more important for inclusion to win, or exclusion? The choice seems like it would have to be pretty arbitrary.
I suggest that it would be better to have a different way of constructing the range. This would have the additional benefit of avoiding all the nil hoops you're talking about jumping through and let users be explicit about the kind of range that they're making.
Perhaps something like:
(defn make-range-checker [bottom-check top-check]
(fn [n]
(and (bottom-check n)
(top-check n))))
So that for your initial 3 examples, you'd do something like these to create range-checking functions that you could apply to your input of 100:
(make-range-checker (partial < 10) (partial >= 100))
(make-range-checker (partial < 10) (partial > 200))
(make-range-checker (partial <= 100) (partial > 101))
(your third example is not correct, incidentally: "100<=101 is not true")
Someone wanting to create a range that extends to infinity in either direction could simply pass a predicate that always returns true.
(make-range-checker (partial < 10) (constantly true))
(make-range-checker (constantly true) (partial > 10))

How to change a value of sub maps of a map?

I am new to Clojure and functional programming and now I am stuck with a problem. I get such a data structure:
{
:service1 \a
:service2 \b
:service3 \c
:default \d
:alert-a {
:duration "00:00-23:59"
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-b {
:duration "09:00-23:00"
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-c {
:duration "00:00-23:59"
:if-alert true
:continuous-times 5
:time-interval [5]
:times 1
}
:alert-d {
:duration "00:00-23:59"
:if-alert true
:continuous-times 5
:time-interval [5 15 30 60]
:times -1
}
}
This is something read from a config file. I want to change all the :duration value to a DateTime object using clj-time. So I can get something like:
{
:service1 \a
:service2 \b
:service3 \c
:default \d
:alert-a {
:duration DateTime Object
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-b {
:duration DateTime Object
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-c {
:duration DateTime Object
:if-alert true
:continuous-times 5
:time-interval [5]
:times 1
}
:alert-d {
:duration DateTime Object
:if-alert true
:continuous-times 5
:time-interval [5 15 30 60]
:times -1
}
}
But the data structure is immutable. This is an easy problem in other languages but now I don't know how to do it after a whole afternoon.
So can anyone give me some suggestions? Am I using a bad data structure? Or this problem can be somehow solved in a functional way.
Although you are working with immutable datastructures, you can easily and efficiently return new datastructures that are based on the originals.
In this case, the simplest (if repetitive) solution would be:
(-> m
(update-in [:alert-a :duration] parse-duration)
(update-in [:alert-b :duration] parse-duration)
(update-in [:alert-c :duration] parse-duration)
(update-in [:alert-d :duration] parse-duration))
The important thing to realize here is that update-in does not mutate the datastructure it's working on. Instead it returns a new datastructure with the modifications applied.
The threading macro -> allows the new datastructure to be threaded through the update-in operations, so that the final returned value is the original datastructure with all of the updates applied.
The parse-duration function would probably look a bit like this:
(defn parse-duration
"Convert duration in HH:MM-HH:MM format"
[s]
(let [[t1 t2] (clojure.string/split s #"-"))
(Period. (clj-time.coerce/to-date-time t1)
(clj-time.coerce/to-date-time t2)))
In functional programming you don't modify collection, but instead create new collection with needed values substituted by new ones. Fortunately, Clojure comes with a bunch of useful functions for this. For your case update-in should work well. It takes a collection (e.g. map), sequence of nested keys and a function to apply to the most nested value defined by key sequence. For example:
> (def m {:a 1 :b 2 :c {:c1 1 :c2 2}})
#'sandbox5448/m
> m
{:a 1, :c {:c1 1, :c2 2}, :b 2}
> (update-in m [:c :c1] str)
{:a 1, :c {:c1 "1", :c2 2}, :b 2}
Note how value 1 from key sequence [:c :c1] was converted to "1".
So, converting :duration field of :alert-a to DateTime is as easy as writing:
> (update-in your-map [:alert-a :duration] string-to-date)
where string-to-date is you converter function.