9
votes

If I have a vector defined as

(def matrix [[1 2 3][4 5 6]])

How in clojure do I access a random element in a vector of vectors? I keep seeing people say online that one of the benefits to using a vector over a list is that you get random access instead of having to recurse through a list but I haven't been able to find the function that allows me to do this. I'm used to in c++ where I could do matrix[1][1] and it would return the second element of the second vector.

Am I stuck having to loop one element at a time through my vector or is there an easier way to access specific elements?

5

5 Answers

17
votes

Vectors are associative, so you can use get-in to access nested vectors, e.g. matrices, by coordinates.

(def matrix [[1 2 3] [4 5 6] [7 8 9]])

(get-in matrix [1 1])
;=> 5
13
votes

Almost like you would do it in C++:

user=> (def matrix [[1 2 3][4 5 6]])
user=> (matrix 1)
[4 5 6]
user=> ((matrix 1) 1)
5

As the docs say:

Vectors implement IFn, for invoke() of one argument, which they presume is an index and look up in themselves as if by nth, i.e. vectors are functions of their indices.

4
votes

Since vectors are associative data structures, you can also use get-in to reach inside with nested indices:

user=> (def matrix [[1 2 3][4 5 6]])
user=> (get-in matrix [1 1])
5
4
votes

The other answers are probably all that you need, but if you're doing 2D indexing a lot--perhaps along with other transformations of 2D numeric structures--you might want to look into the core.matrix library. Switching between different core.matrix implementations with different performance characteristics is usually a one-line change. One of the implementations consists of operations on Clojure vectors. Here's how you would do the double-indexing in core.matrix:

user=> (use 'clojure.core.matrix)
nil
user=> (def m (matrix [[1 2 3][4 5 6]]))
#'user/m
user=> (mget m 1 1)
5

For that particular operation, core.matrix provides no particular advantage. If you want to iterate through the matrix to generate a new one, here's one way to do it:

user=> (emap inc m)
[[2 3 4] [5 6 7]]

Of course it's not very hard to do that with core Clojure functions. Whether core.matrix is useful depends on what you want to do, obviously.

2
votes

You can use the same approach to Mars answer above, with to-array-2d implemented in clojure.core library :)

user> (def a (to-array-2d [[1 2 3][4 5 6]]))
#'user/a
user> (alength a)
2
user> (alength (aget a 0))
3
user> (aget a 0 0)
1
user> (aget a 0 1)
2
user> (aget a 0 2)
3
user> (aget a 1 0)
4
user> (aget a 2 0)
ArrayIndexOutOfBoundsException