iterating over a nested vector

2,025 views
Skip to first unread message

John Sanda

unread,
Apr 8, 2010, 10:58:28 AM4/8/10
to clo...@googlegroups.com
Hi,

I am just getting started with Clojure and with functional programming for matter. I decided that a good exercise would be for to try and port some Java code that I recently wrote. In the Java code I had a 2-D array which basically represents a table of values, and to keep things simple assume that they are just integers. My Java code had to find the greatest value in each column and return those values in an array. For example, given something like, [[1 2 3] [2 5 1] [4 2 6]], I would expect [4 5 6] to be returned. I came up with the following solution in clojure,

(defn num-cols [rows]
  (count (first rows)))

(defn next-col [col rows]
  (rem (inc col) (num-cols rows)))

(defn col-max [col rows]
  (apply max (map #(nth % col) rows)))

(defn col-widths [rows]
  (loop [widths [] col 0]
    (if (= (count widths) (num-cols rows))
      widths
      (recur (conj widths (col-max col rows)) (next-col col rows)))))

I am assuming that there is probably a simpler, more efficient doing this and am hoping someone can point me in the right direction. I would like to do something along these lines. Start with a sequence that contains the values from the first row of my table. For each row in my table, compare the column values in that row  against the corresponding values in the sequence. If any of the columns in the row are greater than the corresponding values in the sequence, then update/replace the sequence with the greater values from that row.

Thanks

- John

Nurullah Akkaya

unread,
Apr 8, 2010, 12:50:48 PM4/8/10
to clo...@googlegroups.com

If you just want max in each group you can use,

(map #(apply max %) [[1 2 3] [2 5 1] [4 2 6]])

this will give you,

(3 5 6)

Regards,
--
Nurullah Akkaya
http://nakkaya.com

John Sanda

unread,
Apr 8, 2010, 1:13:42 PM4/8/10
to clo...@googlegroups.com
That is actually not what I want. Consider the vector a table where each element which is itself a vector a row. And each element of a nested vector is a cell where its index indicates its column. Writing it as follows may make it more clear,

[

  [1 2 3]
  [2 5 1]
  [4 2 6]
]

I am comparing the values in each of the columns, so the result should be [4 5 6] where the first element represents the largest value in the first column, the second element represents the largest value in the second column, etc. I am about half way through the Programming Clojure book. Maybe as I get further along a more concise solution will become apparent.

- John

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

To unsubscribe, reply using "remove me" as the subject.



--

- John

James Reeves

unread,
Apr 8, 2010, 1:38:04 PM4/8/10
to Clojure
On Apr 8, 1:13 pm, John Sanda <john.sa...@gmail.com> wrote:
> [
>   [1 2 3]
>   [2 5 1]
>   [4 2 6]
> ]
>
> I am comparing the values in each of the columns, so the result should be [4
> 5 6] where the first element represents the largest value in the first
> column, the second element represents the largest value in the second
> column, etc.

The `map` function allows you to specify more than one collection.
With multiple collections, the mapping function is passed an argument
for each collection. So:

(map + [1 2 3] [4 5 6])
=> ((+ 1 4) (+ 2 5) (+ 3 6))
=> (5, 7, 9)

By combining this with `apply` and `max`, you can find the maximum
value of each column:

(defn max-columns [coll]
(apply map max coll))

If we pass your vector to max-columns:

(max-columns [[1 2 3] [2 5 1] [4 2 6]])
=> (apply map max [[1 2 3] [2 5 1] [4 2 6]])
=> (map max [1 2 3] [2 5 1] [4 2 6])
=> ((max 1 2 4) (max 2 5 2) (max 3 1 6))
=> (4 5 6)

- James

Nurullah Akkaya

unread,
Apr 8, 2010, 1:51:23 PM4/8/10
to clo...@googlegroups.com

If I got you right this time, if you

(apply interleave [[1 2 3] [2 5 1] [4 2 6]])

you will get,

(1 2 4 2 5 2 3 1 6)

then if you partition by 3,

(partition 3 (apply interleave [[1 2 3] [2 5 1] [4 2 6]]))

you get,

((1 2 4) (2 5 2) (3 1 6))

then as before applying max,

(map #(apply max %)
(partition 3 (apply interleave [[1 2 3] [2 5 1] [4 2 6]])))

will give you,

(4 5 6)

John Sanda

unread,
Apr 8, 2010, 1:55:03 PM4/8/10
to clo...@googlegroups.com
Thanks for the explanation. I did see in the docs that the map function can take multiple collections, but I guess I did not quite understand it. Your explanation really helps illustrate how it works. My Java version of this is probably around 30 lines with several branches/execution paths and variables as opposed to a 1-line clojure version that is free of branching and variables. I'm hooked ;-)

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

To unsubscribe, reply using "remove me" as the subject.



--

- John

Russell Christopher

unread,
Apr 8, 2010, 3:04:11 PM4/8/10
to clo...@googlegroups.com
Another one using for

(defn col-widths [arr] (for [i (range (count arr))] (apply max (map #(nth % i) arr))))

Per Vognsen

unread,
Apr 8, 2010, 10:27:28 PM4/8/10
to clo...@googlegroups.com
Or you can separate concerns a bit more:

(defn transpose [xs]
(apply map vector xs))

Now Nurullah's original suggestion applies:

(map #(apply max %) (transpose xs))

-Per

Russell Christopher

unread,
Apr 9, 2010, 10:44:43 AM4/9/10
to clo...@googlegroups.com
http://rosettacode.org/wiki/Matrix_transposition#Clojure

Does anyone know if transpose exists in core or contrib? A cursory check doesn't reveal it, seems like it should be available.

Thanks

Konrad Hinsen

unread,
Apr 9, 2010, 11:53:51 AM4/9/10
to clo...@googlegroups.com
On 09.04.2010, at 16:44, Russell Christopher wrote:

> http://rosettacode.org/wiki/Matrix_transposition#Clojure
>
> Does anyone know if transpose exists in core or contrib? A cursory check doesn't reveal it, seems like it should be available.

Not that I know. But here's a version that works even for higher nesting levels than 2 (look for nv-transpose):

http://code.google.com/p/clj-multiarray/source/browse/src/multiarray/nested_vectors.clj

Konrad.

Reply all
Reply to author
Forward
0 new messages