Map all elements, preserving vector structures - clojure

For example, how could I best achieve this transformation:
[[[1 2]] [3 4] [[5] 6]] -> [[[2 3]] [4 5] [[6] 7]]
Is there an idiomatic way of doing this, with any number of levels?

You could use clojure.walk to increment numbers in arbitrarily nested structures:
(def data [[[1 2]] [3 4] [[5] 6]])
(clojure.walk/postwalk
#(if (number? %) (inc %) %)
data)
=> [[[2 3]] [4 5] [[6] 7]]

Related

Create 2-dimensional vectors from 1-dimensionals

Although my question seems to be concerned with a rather trivial task, I was not yet able to sucessfully create a single 2-dimensional vector
[[1 2] [3 4]]
fom two individual vectors,
(def a [1 2]) and (def b [3 4]).
Leaving the functions conj and cons aside, I ran into the problem that vec or into-array both expect single input values.
Another workaround would be pre-filling a two dimenstional vector
(vec (replicate 2 (vec (replicate 2 nil))))
but I it is still a complicated option.
Just use vector:
(def a [1 2])
(def b [3 4])
(vector a b) => [[1 2] [3 4]]
[a b] => [[1 2] [3 4]]
There are several ways to approach this:
As #MartinPůda says, use vector: (vector a b) => [[1 2] [3 4]])
Use conj: (conj [] a b) => [[1 2] [3 4]]
Use a vector literal: [a b] => [[1 2] [3 4]]
If Clojure has a problem it is an excess of riches. :-)

Clojure - Applying a Function to a vector of vectors

I have a vector [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]. I want to apply a function to this vector but keep the data structure.
For example I want to add 1 to every number but keep the data structure to get the result being [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]. Is this possible?
I have tried
(map #(+ 1 %) (flatten [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]))
=> (2 3 4 5 6 7 8 9 10 11 12)
But you can see that the data structure is not the same.
Is there maybe a function that takes (2 3 4 5 6 7 8 9 10 11 12) to [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]
I thought maybe to use postwalk but I'm not sure if this is correct.
Any help would be much appreciated
You can use postwalk:
(require '[clojure.walk :as walk])
(let [t [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]]
(walk/postwalk (fn [x] (if (number? x) (inc x) x)) t))
also the classic recursive solution is not much more difficult:
(defn inc-rec [data]
(mapv #((if (vector? %) inc-rec inc) %) data))
#'user/inc-rec
user> (inc-rec [1 [2 3 [4 5] [6 7]] [[8 9] 10]])
;;=> [2 [3 4 [5 6] [7 8]] [[9 10] 11]]
Another way to solve your problem is via Specter. You do need another dependency then, but it can be a helpful library.
(ns your-ns.core
(:require [com.rpl.specter :as specter]))
(def data [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11])
(specter/defprotocolpath TreeWalker) ;; define path walker
(specter/extend-protocolpath TreeWalker
;; stop walking on leafs (in this case long)
Object nil
;; when we are dealing with a vector, TreeWalk all elements
clojure.lang.PersistentVector [specter/ALL TreeWalker])
You can extend it to perform more complicated operations. For this use case normal Clojure is good enough.
(specter/transform [TreeWalker] inc data)
;; => [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]

Clojure - Splitting a vector

If I have two arguments [[1 2] [3 4]] and [5 6], how can I get to [[1 5] [2 6] [3 5] [4 6]].
I thought I may have to use for so I tried,
(for [x [[1 2] [3 4]]]
(for [xx x]
(for [y [5 6]] [xx y])))
But it returned ((([1 5] [1 6]) ([2 5] [2 6])) (([3 5] [3 6]) ([4 5] [4 6])))
Any help would be much appreciated. Thanks
(mapcat #(map vector % [5 6]) [[1 2] [3 4]])
or using for:
(for [c [[1 2] [3 4]]
p (map vector c [5 6])]
p)
If I understood your question correctly, your solution is actually very close. You did not need to nest the for expressions explicitly, since doing so creates a new list at every level, instead, just use multiple bindings:
(for [x [[1 2][3 4]]
xx x
y [5 6]]
[xx y])
Which will result in
([1 5] [1 6] [2 5] [2 6] [3 5] [3 6] [4 5] [4 6])
Edit
Now that I finally read your question carefully, I came up with the same answer as #Lee did (mapcat (fn[x] (map vector x [5 6])) [[1 2] [3 4]]), which is the right one and should probably be accepted.
This is one (hmm - not particularly elegant) way to get your answer, without using for:
(defn f [x y]
(let [x' (apply concat x)
multiples (/ (count x') (count y))
y' (apply concat (repeat multiples y))]
(mapv vector x' y')))
(f [[1 2] [3 4]] [5 6])
;;=> [[1 5] [2 6] [3 5] [4 6]]

Tree traversal in Clojure

Consider a tree, which is defined by the following recursive definition:
a tree should be a vector of two elements: The first one is a number, the second one is either a list of trees or nil.
The following clojure data structure would be an example:
(def tree '[9 ([4 nil]
[6 nil]
[2 ([55 nil]
[22 nil]
[3 ([5 nil])])]
[67 ([44 nil])])])
This structure should be transformed into a list of all possible downwards connections, that can be made from any node to its childs. The connections should be represented as vectors containing the value of the parent node followed by the value of the child node. The order is not important:
(def result '([9 4]
[9 6]
[9 2]
[2 55]
[2 22]
[2 3]
[3 5]
[9 67]
[67 44])
I came up with this solution:
(defn get-connections [[x xs]]
(concat (map #(vector x (first %)) xs)
(mapcat get-connections xs)))
And, indeed:
(= (sort result)
(sort (get-connections tree)))
;; true
However, are there better ways to do so with just plain clojure? In the approach, I'm traversing each node's childs twice, this should be avoided. In this particular case, tail-recursion is not necessary, so a simple recursive version would be ok.
Moreover, I'd be interested which higher level abstractions could be used for solving this. What about Zippers or Clojure/Walk? And finally: Which of those techniques would be available in ClojureScript as well?
you could try combination of list comprehension + tree-seq:
user> (for [[value children] (tree-seq coll? second tree)
[child-value] children]
[value child-value])
;;=> ([9 4] [9 6] [9 2] [9 67] [2 55] [2 22] [2 3] [3 5] [67 44])
this should be available in cljs.
as far as i know, both zippers and clojure.walk are available in clojurescript, but in fact you don't need them for this trivial task. I guess tree-seq is rather idiomatic.
What about the double traversal, you could easily rearrange it into a single one like this:
(defn get-connections [[x xs]]
(mapcat #(cons [x (first %)] (get-connections %)) xs))
user> (get-connections tree)
;;=> ([9 4] [9 6] [9 2] [2 55] [2 22] [2 3] [3 5] [9 67] [67 44])
then you could add laziness, to make this solution truly idiomatic:
(defn get-connections [[x xs]]
(mapcat #(lazy-seq (cons [x (first %)] (get-connections %))) xs))

Clojure: Semi-Flattening a nested Sequence

I have a list with embedded lists of vectors, which looks like:
(([1 2]) ([3 4] [5 6]) ([7 8]))
Which I know is not ideal to work with. I'd like to flatten this to ([1 2] [3 4] [5 6] [7 8]).
flatten doesn't work: it gives me (1 2 3 4 5 6 7 8).
How do I do this? I figure I need to create a new list based on the contents of each list item, not the items, and it's this part I can't find out how to do from the docs.
If you only want to flatten it one level you can use concat
(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
To turn a list-of-lists into a single list containing the elements of every sub-list, you want apply concat as nickik suggests.
However, there's usually a better solution: don't produce the list-of-lists to begin with! For example, let's imagine you have a function called get-names-for which takes a symbol and returns a list of all the cool things you could call that symbol:
(get-names-for '+) => (plus add cross junction)
If you want to get all the names for some list of symbols, you might try
(map get-names-for '[+ /])
=> ((plus add cross junction) (slash divide stroke))
But this leads to the problem you were having. You could glue them together with an apply concat, but better would be to use mapcat instead of map to begin with:
(mapcat get-names-for '[+ /])
=> (plus add cross junction slash divide stroke)
The code for flatten is fairly short:
(defn flatten
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
It uses tree-seq to walk through the data structure and return a sequence of the atoms. Since we want all the bottom-level sequences, we could modify it like this:
(defn almost-flatten
[x]
(filter #(and (sequential? %) (not-any? sequential? %))
(rest (tree-seq #(and (sequential? %) (some sequential? %)) seq x))))
so we return all the sequences that don't contain sequences.
Also you may found useful this general 1 level flatten function I found on clojuremvc:
(defn flatten-1
"Flattens only the first level of a given sequence, e.g. [[1 2][3]] becomes
[1 2 3], but [[1 [2]] [3]] becomes [1 [2] 3]."
[seq]
(if (or (not (seqable? seq)) (nil? seq))
seq ; if seq is nil or not a sequence, don't do anything
(loop [acc [] [elt & others] seq]
(if (nil? elt) acc
(recur
(if (seqable? elt)
(apply conj acc elt) ; if elt is a sequence, add each element of elt
(conj acc elt)) ; if elt is not a sequence, add elt itself
others)))))
Example:
(flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8])))
=>[[1 2] [3 4] [5 6] [7 8]]
concat exampe surely do job for you, but this flatten-1 is also allowing non seq elements inside a collection:
(flatten-1 '(1 2 ([3 4] [5 6]) ([7 8])))
=>[1 2 [3 4] [5 6] [7 8]]
;whereas
(apply concat '(1 2 ([3 4] [5 6]) ([7 8])))
=> java.lang.IllegalArgumentException:
Don't know how to create ISeq from: java.lang.Integer
Here's a function that will flatten down to the sequence level, regardless of uneven nesting:
(fn flt [s] (mapcat #(if (every? coll? %) (flt %) (list %)) s))
So if your original sequence was:
'(([1 2]) (([3 4]) ((([5 6])))) ([7 8]))
You'd still get the same result:
([1 2] [3 4] [5 6] [7 8])