Clojure getting a coordinate from arrays - clojure

I'm having trouble figuring out how I can find a coordinate of a character within an array.
I have small ASCII maps that look like this:
+-----+
|XPX X|
|X X|
|X XX|
|X X|
|XX DX|
+-----+
This map is split up by lines into an array so the array looks like:
["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"]
What I am trying to do is pass a character such as 'P' and find out the coordinate of the character in the map. In this instance 'P' is located at the coordinate (2,3).
As a starting point, I tried to figure out the column of the character using
(doseq [m map] (println (clojure.string/index-of m "P")))
This returned the output of
nil
2
nil
nil
nil
nil
nil
nil
At this point, I am now confused as to how I can just return '2' and the array index that this row is in.

One way to do it - assuming your input is a vector of strings as you have presented, is like so:
(def the-map ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"])
(defn get-index [item coll]
(some #(when ((complement nil?) %) %)
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
This will return the first instance of whatever item you are looking for, e.g. for "+" it would be [0,0]. Also (2,3) is actually (1,2) as clojure uses zero-based indexing.
You can shorten this a little by using keep with identity
(defn get-index [item coll]
(first (keep identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll))))
EDIT:
I realized it's redundant to have (first (keep identity....), instead just use some with identity:
(defn get-index [item coll]
(some identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
Some answers below gave you a way to get every matching coordinate, rather than the first. All you have to do is change some identity to remove nil? in my above version to accomplish this.
For example:
(defn get-index [item coll]
(remove nil?
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
Finally, if it is really your intention to have indexes be one-based, you can just increment each found index:
(defn get-index [item coll]
(some identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [(inc ix) (inc i)])))
coll)))

(def strmap ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"])
(defn find-char-2d
[arr char-to-find]
(some->> (map-indexed (fn [i row]
[i (clojure.string/index-of row char-to-find)])
arr)
(filter second)
first
(map inc)))
(println (find-char-2d strmap "Q")) ; prints "nil"
(println (find-char-2d strmap "P")) ; prints "(2, 3)"
The map-indexed loops through the row strings, searching each one for your substring while keeping track of the row index i. The some->> threading macro passes the result (which is a LazySeq) to the filter, which removes all elements with nil columns. Since we only want the first coordinates (assuming the character you're searching for can only exist once in the map), we select the first element. If the substring doesn't exist in the map, we will get nil, and the the some->> will short-circuit so that nil is returned. Otherwise, the indices will both be incremented (since your coordinates are 1-indexed.
Alternatively, you could linearize the your map, find the index in the linear map, and then calculate the 2d coordinates from the linear index:
(defn find-char-2d
[arr char-to-find]
(some-> (clojure.string/join "" arr)
(clojure.string/index-of char-to-find)
((juxt #(-> (/ % (count arr)) int inc)
#(inc (mod % (count (first arr))))))))

I went a little bit further and wrote a function that find coordinates of all the occurrences of a char.
(let [a ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"]]
(letfn [(find-coords [a ch]
(let [op (fn [f] (comp inc #(f % (count (first a)))))]
(->> a
(clojure.string/join "")
(map-indexed vector)
(filter (comp (partial = ch) second))
(map first)
(map (juxt (op quot) (op rem))))))]
(find-coords a \P)))
=> ([2 3])
(find-coords a \X)
=> ([2 2] [2 4] [2 6] [3 2] [3 6] [4 2] [4 5] [4 6] [5 2] [5 6] [6 2] [6 3] [6 6])

I added another P char to show how to find all of them. Here is a simpler solution using for:
(defn strs->array [strs] (mapv vec strs))
(def data ["+-----+"
"|XPX X|"
"|X X|"
"|X XX|"
"|X P X|"
"|XX DX|"
"+-----+"])
(def data-array (strs->array data))
(defn find-chars-2 [ch-array tgt-char]
(let [num-rows (count ch-array)
num-cols (count (first ch-array))]
(for [ii (range num-rows)
jj (range num-cols)
:let [curr-char (get-in ch-array [ii jj])]
:when (= tgt-char curr-char)]
[ii jj])))
with result:
(find-chars-2 data-array \P)) => ([1 2] [4 3])
Using get-in assumes you have nested vectors, hence the need for strs->array. We also assume the data is rectangular (not ragged) and haven't put in any error-checking that a real solution would need.

i would go with something like this (a bit more generalized)
(def field ["+-----+"
"|XPX X|"
"|X ZX|"
"|X XX|"
"|X X|"
"|XX DX|"
"+-----+"])
(defn find-2d [pred field]
(for [[i line] (map-indexed vector field)
[j ch] (map-indexed vector line)
:when (pred ch)]
[i j ch]))
user> (find-2d #{\P} field)
;;=> ([1 2 \P])
user> (find-2d #{\P \Z} field)
;;=> ([1 2 \P] [2 4 \Z])
user> (find-2d #{\D \P \Z} field)
;;=> ([1 2 \P] [2 4 \Z] [5 4 \D])
user> (find-2d #(Character/isUpperCase %) field)
;;=> ([1 1 \X] [1 2 \P] [1 3 \X] [1 5 \X]
;; [2 1 \X] [2 4 \Z] [2 5 \X] [3 1 \X]
;; [3 4 \X] [3 5 \X] [4 1 \X] [4 5 \X]
;; [5 1 \X] [5 2 \X] [5 4 \D] [5 5 \X])
another one is more functional (although a less readable)
(defn find-2d [pred field]
(filter (comp pred last)
(mapcat #(map vector (repeat %1) (range) %2)
(range)
field)))
works exactly the same as the first one

Related

Extracting two map elements with the largest distance in Clojure

I am trying to extract two elements of a map with the largest distance. For that, I defined the function for calculating the distance and can obtain the distance between the first element (p1) and other elements of the map. But I need to calculate distances between the second item (p2) and the next ones (p3, p4, p5), the third item (p3) and (p4, p5), the fourth item (p4) and fifth item (p5). Then I need to identify the maximum amount between all distances and return the 2 items with the largest distance and the distance itself. Any help is highly appreciated.
Here is my code:
(defn eclid-dist
[u v]
(Math/sqrt (apply + (map #(* % %) (mapv - u v)))))
(def error
{:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(dotimes [i (dec (count error))]
(let [dis (eclid-dist (second (nth (seq error) 0))
(second (nth (seq error) (+ i 1))))
max-error (max dis)]
(println [':dis' dis ':max-error' max-error])))
I tried to save each calculated distance as a vector element separately to prevent overwriting but it was not successful.
You could use the for macro for this. It let's you combine two nested loops to test for all pairs. Then you can use max-key to pick the pair with largest distance:
(defn find-largest-dist-pair [vec-map]
(apply max-key second
(for [[[k0 v0] & r] (iterate rest vec-map)
:while r
[k1 v1] r]
[[k0 k1] (eclid-dist v0 v1)])))
(find-largest-dist-pair error)
;; => [[:p3 :p4] 10.392304845413264]
There is nothing wrong with eclid-dist, you could just use the dedicated Clojure library clojure.math (and ->> thread-last macro for better readability) and rewrite it like this:
(:require [clojure.math :as m])
(defn distance [u v]
(->> (mapv - u v)
(mapv #(m/pow % 2))
(reduce +)
m/sqrt))
Your main problem is, how to create unique pairs of points from your data. You could write a recursive function for this:
(defn unique-pairs [point-seq]
(let [[f & r] point-seq]
(when (seq r)
(concat (map #(vector f %) r)
(unique-pairs r)))))
(def error {:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(unique-pairs (vals error))
or use library clojure.math.combinatorics:
Dependency: [org.clojure/math.combinatorics "0.1.6"]
(:require [clojure.math.combinatorics :as combi])
(combi/combinations (vals error) 2)
Note that these functions have slightly different results- it doesn't affect the final result, but if you can, you should use combinations.
Now, you have to compute distance for all these pairs and return the pair with the largest one:
(defn max-distance [point-map]
(->> (combi/combinations (vals point-map) 2)
(map (fn [[u v]] {:u u :v v :distance (distance u v)}))
(apply max-key :distance)))
(max-distance error)
=> {:u [1 2 3], :v [7 8 9], :distance 10.392304845413264}

Clojure - Function that returns all the indices of a vector of vectors

If I have a vector [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]]
How can I return the positions of each element in the vector?
For example 1 has index [0 0 0], 2 has index [0 0 1], etc
I want something like
(some-fn [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] 1)
=> [0 0 0]
I know that if I have a vector [1 2 3 4], I can do (.indexOf [1 2 3 4] 1) => 0 but how can I extend this to vectors within vectors.
Thanks
and one more solution with zippers:
(require '[clojure.zip :as z])
(defn find-in-vec [x data]
(loop [curr (z/vector-zip data)]
(cond (z/end? curr) nil
(= x (z/node curr)) (let [path (rseq (conj (z/path curr) x))]
(reverse (map #(.indexOf %2 %1) path (rest path))))
:else (recur (z/next curr)))))
user> (find-in-vec 11 data)
(1 0 1)
user> (find-in-vec 12 data)
(1 1 0)
user> (find-in-vec 18 data)
nil
user> (find-in-vec 8 data)
(0 2 1)
the idea is to make a depth-first search for an item, and then reconstruct a path to it, indexing it.
Maybe something like this.
Unlike Asthor's answer it works for any nesting depth (until it runs out of stack). Their answer will give the indices of all items that match, while mine will return the first one. Which one you want depends on the specific use-case.
(defn indexed [coll]
(map-indexed vector coll))
(defn nested-index-of [coll target]
(letfn [(step [indices coll]
(reduce (fn [_ [i x]]
(if (sequential? x)
(when-let [result (step (conj indices i) x)]
(reduced result))
(when (= x target)
(reduced (conj indices i)))))
nil, (indexed coll)))]
(step [] coll)))
(def x [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]])
(nested-index-of x 2) ;=> [0 0 1]
(nested-index-of x 15) ;=> [2 1 0]
Edit: Target never changes, so the inner step fn doesn't need it as an argument.
Edit 2: Cause I'm procrastinating here, and recursion is a nice puzzle, maybe you wanted the indices of all matches.
You can tweak my first function slightly to carry around an accumulator.
(defn nested-indices-of [coll target]
(letfn [(step [indices acc coll]
(reduce (fn [acc [i x]]
(if (sequential? x)
(step (conj indices i) acc x)
(if (= x target)
(conj acc (conj indices i))
acc)))
acc, (indexed coll)))]
(step [] [] coll)))
(def y [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15 [16 17 4]]]])
(nested-indices-of y 4) ;=> [[0 1 0] [2 1 1 2]]
Vectors within vectors are no different to ints within vectors:
(.indexOf [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] [[14] [15]])
;;=> 2
The above might be a bit difficult to read, but [[14] [15]] is the third element.
Something like
(defn indexer [vec number]
(for [[x set1] (map-indexed vector vec)
[y set2] (map-indexed vector set1)
[z val] (map-indexed vector set2)
:when (= number val)]
[x y z]))
Written directly into here so not tested. Giving more context on what this would be used for might make it easier to give a good answer as this feels like something you shouldn't end up doing in Clojure.
You can also try and flatten the vectors in some way
An other solution to find the path of every occurrences of a given number.
Usually with functional programming you can go for broader, general, elegant, bite size solution. You will always be able to optimize using language constructs or techniques as you need (tail recursion, use of accumulator, use of lazy-seq, etc)
(defn indexes-of-value [v coll]
(into []
(comp (map-indexed #(if (== v %2) %1))
(remove nil?))
coll))
(defn coord' [v path coll]
(cond
;; node is a leaf: empty or coll of numbers
(or (empty? coll)
(number? (first coll)))
(when-let [indexes (seq (indexes-of-value v coll))]
(map #(conj path %) indexes))
;; node is branch: a coll of colls
(coll? (first coll))
(seq (sequence (comp (map-indexed vector)
(mapcat #(coord' v (conj path (first %)) (second %))))
coll))))
(defn coords [v coll] (coord' v [] coll))
Execution examples:
(def coll [[2 1] [] [7 8 9] [[] [1 2 2 3 2]]])
(coords 2 coll)
=> ([0 0] [3 1 1] [3 1 2] [3 1 4])
As a bonus you can write a function to test if paths are all valid:
(defn valid-coords? [v coll coords]
(->> coords
(map #(get-in coll %))
(remove #(== v %))
empty?))
and try the solution with input generated with clojure.spec:
(s/def ::leaf-vec (s/coll-of nat-int? :kind vector?))
(s/def ::branch-vec (s/or :branch (s/coll-of ::branch-vec :kind vector?
:min-count 1)
:leaf ::leaf-vec))
(let [v 1
coll (first (gen/sample (s/gen ::branch-vec) 1))
res (coords v coll)]
(println "generated coll: " coll)
(if-not (valid-coords? v coll res)
(println "Error:" res)
:ok))
Here is a function that can recursively search for a target value, keeping track of the indexes as it goes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn index-impl
[idxs data tgt]
(apply glue
(for [[idx val] (zip (range (count data)) data)]
(let [idxs-curr (append idxs idx)]
(if (sequential? val)
(index-impl idxs-curr val tgt)
(if (= val tgt)
[{:idxs idxs-curr :val val}]
[nil]))))))
(defn index [data tgt]
(keep-if not-nil? (index-impl [] data tgt)))
(dotest
(let [data-1 [1 2 3]
data-2 [[1 2 3]
[10 11]
[]]
data-3 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 13]]
[[20]
[21]]
[[30]]
[[]]]
]
(spyx (index data-1 2))
(spyx (index data-2 10))
(spyx (index data-3 13))
(spyx (index data-3 21))
(spyx (index data-3 99))
))
with results:
(index data-1 2) => [{:idxs [1], :val 2}]
(index data-2 10) => [{:idxs [1 0], :val 10}]
(index data-3 13) => [{:idxs [1 1 1], :val 13}]
(index data-3 21) => [{:idxs [2 1 0], :val 21}]
(index data-3 99) => []
If we add repeated values we get the following:
data-4 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 2]]
[[20]
[21]]
[[30]]
[[2]]]
(index data-4 2) => [{:idxs [0 0 1], :val 2}
{:idxs [1 1 1], :val 2}
{:idxs [4 0 0], :val 2}]

Clojure: Cycle through a collection while looping through another collection?

I have two collections x and y, both having different number of items. I want to loop through x and do something with a side-effect, while cycling through y. I don't want to repeat y while looping through x. Both doseq and for repeats y:
(for [x (range 5)
y ["A" "B"]]
[x y])
This produces ([0 "A"] [0 "B"] [1 "A"] [1 "B"] [2 "A"] [2 "B"] [3 "A"] [3 "B"] [4 "A"] [4 "B"]).
What I want is something that will produce: ([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"]).
Background, I have lines from a file and core.async channels (say 5) and I want to put each line to the next channel in my collection, something like:
(defn load-data
[file chans]
(with-open [rdr (io/reader file)]
(go
(doseq [l (line-seq rdr)
ch chans]
(>! ch l)))))
If you pass multiple sequences to map it steps through each of them in lock step calling the mapped function with the value from the current position in each. Stopping when one of the sequences runs out.
user> (map vector (range 5) (cycle ["A" "B"]))
([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"])
In this case the sequence from (cycle ["A" "B"]) will keep producing As and Bs forever though map will stop consuming them when the sequence from (range 5) ends. each step then calls the vector function with these two arguments and adds the result to the output sequence.
and for the second example using a go-loop is a fairly standard way of fanning out an input sequence:
user> (require '[clojure.core.async :refer [go go-loop <! <!! >!! >! chan close!]])
nil
user> (defn fanout [channels file-lines]
(go-loop [[ch & chans] (cycle channels)
[line & lines] file-lines]
(if line
(do
(>! ch line)
(recur chans lines))
(doseq [c channels]
(close! c)))))
#'user/fanout
user> (def lines ["first" "second" "third" "fourth" "fifth"])
#'user/lines
user> (def test-chans [(chan) (chan) (chan)])
#'user/test-chans
user> (fanout test-chans lines)
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#3b363fc5>
user> (map <!! test-chans)
("first" "second" "third")
user> (map <!! test-chans)
("fourth" "fifth" nil)

How to transpose a nested vector in clojure

I have the following variable
(def a [[1 2] [3 4] [5 6]])
and want to return
[[1 3 5][2 4 6]]
and if input is
[[1 2] [3 4] [5 6] [7 8 9]] then the required result is
[[1 3 5 7] [2 4 6 8] [9]]
How to do it in clojure?
(persistent!
(reduce
(fn [acc e]
(reduce-kv
(fn [acc2 i2 e2]
(assoc! acc2 i2 ((fnil conj []) (get acc2 i2) e2)))
acc
e))
(transient [])
[[1 2 3] [:a :b] [\a] [111] [200 300 400 500]]))
;;=> [[1 :a \a 111 200] [2 :b 300] [3 400] [500]]
An empty vector can be updated via the update-in fn at the 0th index, a non-empty vector can be, additionally, updated at the index immediately following the last value.
The reduction here is about passing the outer accumulator to the inner reducing function, updating it accordingly, and then returning it back to the outer reducing function, which in turn will pass again to the inner rf for processing the next element.
EDIT: Updated to fastest version.
I like ifett's implementation, though it seems weird to use reduce-kv to build a vector that could be easily build with map/mapv.
So, here is how I would've done it:
(defn transpose [v]
(mapv (fn [ind]
(mapv #(get % ind)
(filter #(contains? % ind) v)))
(->> (map count v)
(apply max)
range)))
(->> (range)
(map (fn [i]
(->> a
(filter #(contains? % i))
(map #(nth % i)))))
(take-while seq))
Notice that this algorithm creates a lazy seq of lazy seqs so you that you will only pay for the transformations you really consume. If you insist on creating vectors instead, wrap the forms in vec at the necessary places - or if you are using Clojurescript or don't mind a Clojure 1.7 alpha use transducers to create vectors eagerly without paying for laziness or immutability:
(into []
(comp
(map (fn [i]
(into [] (comp (filter #(contains? % i))
(map #(nth % i)))
a)))
(take-while seq))
(range))
I find this easy to understand:
(defn nth-column [matrix n]
(for [row matrix] (nth row n)))
(defn transpose [matrix]
(for [column (range (count (first matrix)))]
(nth-column matrix column)))
(transpose a)
=> ((1 3 5) (2 4 6))
nth-column is a list comprehension generating a sequence from the nth element of each sequence (of rows).
Then transpose-matrix is simply iterating over the columns creating a sequence element for each, consisting of (nth-column matrix column) i.e. the sequence of elements for that column.
(map
(partial filter identity) ;;remove nil in each sub-list
(take-while
#(some identity %) ;;stop on all nil sub-list
(for [i (range)]
(map #(get % i) a)))) ;; get returns nil on missing values
Use get to have nil on missing values, iterate (for) on an infinite range, stop on all nil sub-list, remove nil from sub-lists. Add vector constructor before first map and in it's function (first argument) if you really need vectors.
EDIT: please leave a comment if you think this is not useful. We can all learn from mistakes.

Apply a list of functions to a corresponding list of data in Clojure

So I have a list of functions and a list of data:
[fn1 fn2 fn3] [item1 item2 item3]
What can I do to apply each function to its corresponding data item:
[(fn1 item1) (fn2 item2) (fn3 item3)]
Example:
[str #(* 2 %) (partial inc)] [3 5 8]
=> ["3" 10 9]
You can use map
(map #(%1 %2) [str #(* 2 %) (partial inc)] [3 5 8])
("3" 10 9)
If you need a vector back, you can (apply vector ...)
(apply vector (map #(%1 %2) [str #(* 2 %) (partial inc)] [3 5 8]))
["3" 10 9]
Disclaimer: I don't know much Clojure, so there would probably be better ways to do this.
An alternative, not necessarily better:
user=> (for [[f x] (map vector [neg? pos? number?] [1 2 "foo"])]
#_=> (f x))
(false true false)
To make the map version suitable for varargs:
user=> (map (fn [f & args] (apply f args)) [+ - *] [1 2 3] [4 5 6] [7 8 9])
(12 -11 162)