I'm trying to fill a Clojure vector with values from a map.
I have another vector of specific keys in the map the store the values I need.
I need to iterate the key-vec, get the values from the map and store them in another vector.
I've tried using loop+recur:
(let [keys-vec (:keys-vec item)
my-map (:my-map item)]
(loop [newV []
i 0]
(if (< i (count keys-vec))
(recur
(conj newV (get my-map (get keys-vec i)))
(inc i))
newV)))
And it worked.
But I know Clojure is known for it's minimalistic/efficient code writing style and I wanted to know whether there's a better way.
Any ideas?
I'd say, that the most idiomatic way will be to use original vector. I don't see any reason to explicitly clone an immutable datastructure.
You want the select-keys function to extract only the keys of interest from your map. See: http://clojuredocs.org/clojure.core/select-keys
Then, use the vals function to extract all of the values from the filtered map:
> (def my-map {:a 1 :b 2 :c 3})
> (def my-keys [:a :b])
> (select-keys my-map my-keys)
{:a 1, :b 2}
> (def filtered-map (select-keys my-map my-keys))
> filtered-map
{:a 1, :b 2}
> (vals filtered-map)
(1 2)
You should keep a browser tab open to the Clojure Cheatsheet at all times. It is invaluable for finding the functions you want. Keep studying it repeatedly, as you'll keep finding new things for years. This is my favorite version:
http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-cdocs-summary.html
Using Alan Thompson's example:
(def my-map {:a 1 :b 2 :c 3})
(def my-keys [:a :b])
... a simple and fast solution is
(mapv my-map my-keys)
;[1 2]
The alternative
(vals (select-keys my-map my-keys))
;(2 1)
... may not (and, in this case, does not) maintain the order given by my-keys.
Related
Is there a better way of implementing nested loops in clojure?
As a beginner I have written this code of nested loop for comparing difference between dates in days.
Comparing this with nested loops in java using for or while.
(def my-vector [{:course-type "clojure"
:start-date "2021-01-25"
:end-date "2021-02-06"}
{:course-type "r"
:start-date "2021-01-15"
:end-date "2021-02-06"}
{:course-type "python"
:start-date "2020-12-05"
:end-date "2021-01-05"}
{:course-type "java"
:start-date "2020-09-15"
:end-date "2020-10-20"}
])
(defn find-gap-in-course [mycourses]
(println "Finding gap between dates....")
(loop [[course1 & mycourses] mycourses]
(loop [[course2 & mycourses] mycourses]
(when (and
(and (not-empty course1) (not-empty course2))
(> (-> java.time.temporal.ChronoUnit/DAYS
(.between
(LocalDate/parse (course2 :end-date))
(LocalDate/parse (course1 :start-date)))) 30))
(println "Dates evaluated are =" (course2 :end-date) (course1 :start-date))
(println "Gap of > 30 days between dates ="
(-> java.time.temporal.ChronoUnit/DAYS
(.between
(LocalDate/parse (course2 :end-date))
(LocalDate/parse (course1 :start-date)))))
(do true)))
(do false)
(if course1 (recur mycourses))))
(find-gap-in-course my-vector)
Learning to program in Clojure requires that one learn to think a bit differently because the tricks and techniques which people become accustomed to using in imperative programming may not serve as well in Clojure. For example in a nested loop, such as you've shown above, what are you trying to do? You're trying to match all of the elements of mycourses against one another and do some processing. So let's define a function which gives us back all the combinations of elements in a collection 1:
(defn combos[c] ; produce all combinations of elements in a collection
(for [x c y c] (vector x y)))
This is a very simple function which matches all the elements of a collection against one another and returns the accumulated pairings. For example, if you invoke
(combos [1 2 3])
you'll get back
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3])
This will work with any collection. If you invoke combos as
(combos '("abc" 1 [0 9]))
you'll get back
(["abc" "abc"] ["abc" 1] ["abc" [0 9]] [1 "abc"] [1 1] [1 [0 9]] [[0 9] "abc"] [[0 9] 1] [[0 9] [0 9]])
So I think you can see where we're going here. Rather than running a nested loop against a collection, you can just create a collection of combinations of elements and run a simple loop over those combinations:
(defn find-gap-in-course [mycourses]
(loop [course-combos (combos mycourses)]
(let [combi (first course-combos)
[course1 course2] combi]
; ...processing of course1 and course2 here...
(recur (rest mycourses)))))
But what if we don't want to consider the cases where a course is matched against itself? In that case another function to only return the desired cases is useful:
(defn different-combos [c] ; all combinations where [1] <> [2]
(filter #(not= (% 0) (% 1)) (combos c)))
Use whatever works best for you.
1 About here the Clojure cognoscenti are probably screaming "NO! NO! Use clojure.math.combinatorics!". When teaching I like to give useful examples which the student can see, read, and learn from. YMMV.
Here is how I would write the above code, starting from my favorite template project. I have included some unit tests to illustrate what is occurring in the code:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:import
[java.time LocalDate]))
(defn days-between
"Find the (signed) interval in days between two LocalDate strings."
[localdate-1 localdate-2]
(.between java.time.temporal.ChronoUnit/DAYS
(LocalDate/parse localdate-1)
(LocalDate/parse localdate-2)))
(dotest ; from tupelo.test
(is= -5 (days-between "2021-01-25" "2021-01-20"))
(is= 5 (days-between "2021-01-25" "2021-01-30")))
(defn course-pairs-with-30-day-gap
"Return a list of course pairs where the start date of the first course
is at least 30 days after the end of the second."
[courses]
(for [c1 courses
c2 courses
:let [start-1 (:start-date c1)
end-2 (:end-date c2)
gap-days (days-between end-2 start-1)]
:when (< 30 gap-days)]
[(:course-type c1) (:course-type c2) gap-days]))
with result
(dotest
(let [all-courses [{:course-type "clojure"
:start-date "2021-01-25"
:end-date "2021-02-06"}
{:course-type "r"
:start-date "2021-01-15"
:end-date "2021-02-06"}
{:course-type "python"
:start-date "2020-12-05"
:end-date "2021-01-05"}
{:course-type "java"
:start-date "2020-09-15"
:end-date "2020-10-20"}]]
(is= (course-pairs-with-30-day-gap all-courses)
[["clojure" "java" 97]
["r" "java" 87]
["python" "java" 46]])))
In the output, I left the names of course-1, course-2, and the gap in days to verify the calculation is the intended one. This could be modified or extended for production use, of course.
In clojure we normally use pre-existing functions like for (technically a macro) instead of low-level tools like loop/recur. The modifiers :let and :when make them extra-powerful for analyzing & transforming data structures.
Please see this list of documentation sources,
especially books like Getting Clojure and the Clojure CheatSheet.
I've been writing out a function in Clojure that would take a row of a 2d array and then multiples the values in it by a single value. I have the index to get the specific row and the value to multiply the row with in another array.
The function will return the array, that has now got values multipled by the single value.
Here's the code:
(def twod-array [[3.1 0.0023 0.35]
[0.21 0.00353 8.13]])
(def iandv [1 3.1])
(defn array-multiply [iandv twod-array]
(
let [array-row (nth twod-array (nth iandv 0))]
(map * [array-row] [(nth iandv 1)])
)
The let gets the array row and then it will return the row with the values inside multiplied with the value of the "index and value" array.
This has the closest I've gotten using the examples with the clojure.doc website and I'm getting a ClassCastException of the following:
ClassCastException clojure.lang.PersistentVector cannot be cast to java.lang.Number clojure.lang.Numbers.multiply (Numbers.java:148)
I've been looking at map vector and other map functions but I haven't been able to find a good solution.
more clojurish way could look something like this:
(defn array-multiply [[row mul] twod-array]
(update twod-array row #(mapv (partial * mul) %)))
user> (array-multiply iandv twod-array)
;;=> [[3.1 0.0023 0.35] [0.651 0.010943000000000001 25.203000000000003]]
Your code is somewhat hard to read, but basically, you're trying to multiply a number and a vector, that doesn't work.
(defn array-multiply [iandv twod-array]
(let [array-row (nth twod-array (nth iandv 0))]
(map * array-row [(nth iandv 1)])))
works since array-row already is a vector.
I have a 2D vector that looks like this
(["2011-01-01" "2011" "01" "01"]
["1869-01-01" "1869" "01" "01"]
["1922-01-01" "1922" "01" "01"]
["1905-01-01" "1905" "01" "01"])
I want to have just a vector of just the second column so it will be like this
("2011" "1869" "1922" "1905")
What is the best way to do this in Clojure?
Be sure to always consult the Clojure CheatSheet for questions like this.
For something this simple, just use mapv (or map) and the second function:
(def data [["2011-01-01" "2011" "01" "01"]
["1869-01-01" "1869" "01" "01"]
["1922-01-01" "1922" "01" "01"]
["1905-01-01" "1905" "01" "01"]])
(mapv second data) => ["2011" "1869" "1922" "1905"]
I prefer mapv since it gives the result as a vector (non-lazy) which is easier to cut & paste w/o needing quotes.
If you have more complicated needs, you may wish to review the tupelo.array library. Your data is already in the form of a Clojure 2-D array (vector of vectors), so you can just use the col-get function:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[tupelo.array :as ta]))
(def data ...)
; Note zero-based indexing
(ta/col-get data 1) => ["2011" "1869" "1922" "1905"]
The library includes many other functions for input/output, get/set values, flip arrays up/down or left/right, rotation, adding/deleting rows & cols, etc. There is also a version that works with mutable arrays (Java native object arrays) instead of Clojure immutable data structures.
Possible answers
A. (reduce (fn [| [_ - ]] (conj | -)) [] data)
B. (map (fn [[_ - ]] -) data)
C. (map second data)
generally i want to know when we have array of object that have some property can same the "Object Literal in JavaScript" can calculated with specific function. i want to create that property for my array in clojure to apply some calculation on them such as sorting or more simpler finding maximum according to that property.for example how try find maximum in this example?
(def aSqh (fn [x] (* x x)))
(def maSqh (max (apply aSqh [1 2 3 4])))
the have error that output is object and not number
You seem to be thinking of a mapping operation (take a function of one argument and a collection, replace every element with the result of the function on that element), which in Clojure is called map. apply is a function for plumbing collections into functions as if they were given each element as a separate argument. Usually you want to use it with variadic functions (i.e. functions such as max, that take a variable number of arguments). For instance
(def maSqh (apply max (map aSqh [1 2 3 4]))) ;;=> 16
If you want to preserve the datatype of a collection after performing a mapping, you can use into and empty:
(defn preserving-map [f coll]
(into (empty coll) (map f coll)))
(preserving-map aSqh [1 2 3 4]) ;;=>[1 4 9 16]
(preserving-map aSqh #{1 2 3 4}) ;;=> #{1 4 9 16}
but this removes the (useful) laziness that map usually gives us. For the particular case of vectors (like [1 2 3 4]), this use case is common enough that there is mapv which eagerly performs mappings and puts them into a vector.
I'm trying to parse a nested array structure of the following form:
[element [[child1] [child2] [child3 [[subchild1] [subchild2]]]]]
I would also like to return a list with all symbols (and nothing else), regardless of nesting depth; however, I'm not looking for flatmap or flatten etc, since I need to perform more complicated additional work on every element.
This is what I came up with so far:
(defn create-element [rs element]
(if (symbol? element)
(cons element rs)
rs))
(defn parse
([rs element] (create-element rs element))
([rs element [children & more]] (if (nil? more)
(parse (parse rs element) (first children))
(parse (parse rs element) (first children) more))))
(defn n-parse [element]
(apply parse () element))
This works fine for the following input:
=> (n-parse ['bla [['asd] ['kkk] ['sss]]])
(sss kkk asd bla)
But this doesn't work:
=> (n-parse ['bla [['asd] ['kkk [['ooo]]] ['sss]]])
(sss kkk asd bla)
I'm still trying to wrap around my head around the types but can't seem to manage to get it right. For example, Haskell makes this easy with pattern matching etc, whereas Clojure doesn't allow same arity function overloading.
Also is there a more concise / idiomatic way (without having to resort to if?) I'd prefer pure Clojure solutions (no external libs) since this is actually for a Clojurescipt project.
Many thanks for any help!
I don't see whats wrong with flatten. If you want to do some work on the items first, do the work first and then flatten the result:
(defn map-tree
"Example: (map-tree + [1 2 [3 5]] [3 4 [5 6]])"
[f & trees]
(if (every? coll? trees)
(apply map (partial map-tree f) trees)
(apply f trees)))
(defmulti transformator identity)
;; transform 'sss element into something special
(defmethod transformator 'sss [_] "sss")
;; default transformation
(defmethod transformator :default [v] v)
Test:
user> (flatten (map-tree transformator '[bla [[asd] [kkk] [sss]]]))
(bla asd kkk "sss")
user>
Would that not work?