Collections: Vector
Overview
From https://clojure.org/reference/data_structures#Vectors:
A Vector is a collection of values indexed by contiguous integers. Vectors support access to items by index in log32N hops. count is O(1). conj puts the item at the end of the vector. Vectors also support rseq, which returns the items in reverse order. 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. Vectors are compared first by length, then each element is compared in order.
Vector Creation
Code
(ns collections-vectors.core)
;; ---
;; Vector creation
;; ---
[1 2 3 4 5]
;; => [1 2 3 4 5]
(vector 1 2 3 4 5)
;; => [1 2 3 4 5]
(vec '(1 2 3 4 5))
;; => [1 2 3 4 5]
(class [1 2 3 4 5])
;; => clojure.lang.PersistentVector
(= [1 2 3 4 5] (vector 1 2 3 4 5))
;; => true
[1 "b" 3 "d"]
;; => [1 "b" 3 "d"]
(cons 1 [2 3 4 5])
;; => (1 2 3 4 5)
(conj [1 2 3 4] 5)
;; => [1 2 3 4 5]
Explanation
Here’s a breakdown of the Clojure code provided:
(ns collections-vectors.core)
- This is a namespace declaration. It’s how Clojure organizes its code. Here, it is creating or referring to a namespace called “collections-vectors.core”.[1 2 3 4 5]
- This is a vector, a basic data structure in Clojure. It contains the elements 1, 2, 3, 4, and 5.(vector 1 2 3 4 5)
- This creates a vector using thevector
function. It will create the same vector as above, [1 2 3 4 5].(vec '(1 2 3 4 5))
- This converts a list (represented by ’ before the parentheses) to a vector using thevec
function.(class [1 2 3 4 5])
- This retrieves the class of the vector [1 2 3 4 5], which isclojure.lang.PersistentVector
. Clojure uses this underlying Java class to implement its vector data structure.(= [1 2 3 4 5] (vector 1 2 3 4 5))
- This checks if two vectors are equal. In this case, it’s checking if the vector [1 2 3 4 5] is equal to the result of(vector 1 2 3 4 5)
, which is also [1 2 3 4 5]. The result is true.[1 "b" 3 "d"]
- This is a vector containing mixed types. It’s valid in Clojure to have a collection of mixed types.(cons 1 [2 3 4 5])
- Thecons
function adds an element to the front of a list or a sequence, not a vector. Therefore, it converts the vector to a sequence and then adds the element, resulting in a sequence (1 2 3 4 5), not a vector.(conj [1 2 3 4] 5)
- Theconj
function adds an element to a collection. In the case of a vector, it adds the element to the end, creating the vector [1 2 3 4 5].
Accessing Elements
Code
(ns collections-vectors.core)
;; ---
;; Accessing elements
;; ---
(first [1 2 3 4 5])
;; => 1
(first [])
;; => nil
(second [1 2 3 4 5])
;; => 2
(second [])
;; => nil
(subvec [1 2 3 4 5] 1 3)
;; => [2 3]
(subvec [1 2 3 4 5] 1)
;; => [2 3 4 5]
(subvec [1] 1)
;; => []
(try
(subvec [1] 2)
(catch Exception _e "IndexOutOfBoundsException"))
;; => "IndexOutOfBoundsException"
(nth [1 2 3 4 5] 0)
;; => 1
(try
(nth [] 0)
(catch Exception _e "IndexOutOfBoundsException"))
;; => "IndexOutOfBoundsException"
(last [1 2 3 4 5])
;; => 5
(last [1])
;; => 1
(last [])
;; => nil
Explanation
This Clojure code demonstrates how to access elements within vectors. The different functions perform the following:
(first [1 2 3 4 5])
- This line returns the first element of the vector, in this case,1
.(first [])
- This line attempts to return the first element of an empty vector, which isnil
as there are no elements to return.(second [1 2 3 4 5])
- This line returns the second element of the vector, in this case,2
.(second [])
- This line attempts to return the second element of an empty vector, which isnil
as there are no elements to return.(subvec [1 2 3 4 5] 1 3)
- This line returns a sub-vector that starts from index1
(0-indexed) and ends before index3
. So, it returns[2 3]
.(subvec [1 2 3 4 5] 1)
- This line returns a sub-vector that starts from index1
and extends till the end of the vector, thus returning[2 3 4 5]
.(subvec [1] 1)
- This line attempts to return a sub-vector starting from index1
of a vector with only one element. There’s no element at index1
, so it returns an empty vector[]
.(try (subvec [1] 2) (catch Exception _e "IndexOutOfBoundsException"))
- This line attempts to return a sub-vector starting from index2
of a vector with only one element. As there’s no element at index2
, it throws anIndexOutOfBoundsException
, which is caught and returns the string “IndexOutOfBoundsException”.(nth [1 2 3 4 5] 0)
- This line returns the element at index0
(0-indexed) in the vector, in this case1
.(try (nth [] 0) (catch Exception _e "IndexOutOfBoundsException"))
- This line attempts to return the element at index0
in an empty vector. As there’s no element at index0
, it throws anIndexOutOfBoundsException
, which is caught and returns the string “IndexOutOfBoundsException”.(last [1 2 3 4 5])
- This line returns the last element of the vector, in this case5
.(last [1])
- This line returns the vector’s last (and only) element, in this case1
.(last [])
- This line attempts to return the last element of an empty vector, which isnil
as there are no elements to return.
Manipulating Vectors
Code
(ns collections-vectors.core)
;; ---
;; Manipulating vectors
;; ---
(def vector-a [1 2 3 4 5])
(def vector-b [6 7 8 9 10])
(concat vector-a vector-b)
;; => (1 2 3 4 5 6 7 8 9 10)
(concat vector-a)
;; => (1 2 3 4 5)
(concat vector-a [])
;; => (1 2 3 4 5)
(flatten [[1 2 3] [4 5 6 [7 8 9]] [10 11 12] 13])
;; => (1 2 3 4 5 6 7 8 9 10 11 12 13)
(map (fn [item] (+ item 1)) vector-a)
;; => (2 3 4 5 6)
(map inc vector-a)
;; => (2 3 4 5 6)
vector-a
;; => [1 2 3 4 5]
(filter (fn [item] ((fn [item] (even? item)) item)) vector-a)
;; => (2 4)
(filter (fn [item] (even? item)) vector-a)
;; => (2 4)
(reduce (fn [acc item] (+ acc item)) 0 vector-a)
;; => 15
(reduce + 0 vector-a)
;; => 15
Explanation
Here’s the line-by-line explanation of the provided Clojure code:
(ns collections-vectors.core)
- This is a namespace declaration. It defines a new namespace namedcollections-vectors.core
.(def vector-a [1 2 3 4 5])
- This line defines a vector namedvector-a
that contains the numbers 1 through 5.(def vector-b [6 7 8 9 10])
- Similarly, this defines another vector,vector-b
, with numbers 6 through 10.(concat vector-a vector-b)
- Theconcat
function concatenates lists or vectors. This expression concatenatesvector-a
andvector-b
, yielding a sequence(1 2 3 4 5 6 7 8 9 10)
.(concat vector-a)
- Ifconcat
is given a single sequence, it will return that sequence. Here, it returns(1 2 3 4 5)
.(concat vector-a [])
- Even if the second sequence is empty,concat
will still return the first sequence. Again, it returns(1 2 3 4 5)
.(flatten [[1 2 3] [4 5 6 [7 8 9]] [10 11 12] 13])
- Theflatten
function takes a sequence and “flattens” it, removing any nested sequences and leaving only their elements. The output of this operation is(1 2 3 4 5 6 7 8 9 10 11 12 13)
.(map (fn [item] (+ item 1)) vector-a)
-map
applies a function to every element of a sequence. This line applies the anonymous function(fn [item] (+ item 1))
, which increments its argument by 1, to each element ofvector-a
, resulting in(2 3 4 5 6)
.(map inc vector-a)
- This is another way of accomplishing the same thing as the previous line.inc
is a built-in function that increments its argument, so the result is again(2 3 4 5 6)
.vector-a
- This is just the vectorvector-a
, which the previous operations have not changed, so it’s still[1 2 3 4 5]
.(filter (fn [item] ((fn [item] (even? item)) item)) vector-a)
-filter
applies a predicate function to each sequence element and returns a new sequence containing only the elements for which the predicate returned true. This line applies the anonymous function(fn [item] (even? item))
, which tests if its argument is even, to each element ofvector-a
. The elements for which this is true are 2 and 4, so the result is(2 4)
.(filter (fn [item] (even? item)) vector-a)
- This line is the same as the previous one, written more concisely. The result is again(2 4)
.(reduce (fn [acc item] (+ acc item)) 0 vector-a)
-reduce
applies a function to the elements of a sequence in a cumulative way. This line applies the anonymous function(fn [acc item] (+ acc item))
, which adds its arguments to the elements ofvector-a
, starting with an initial accumulator value of 0. The result is the sum of the elements, which is 15.(reduce + 0 vector-a)
- This line accomplishes the same thing as the previous line, using the built-in function+
instead of an anonymous function. The result is again 15.
Predicates
Code
(ns collections-vectors.core)
;; ---
;; Predicates
;; ---
(empty? [])
;; => true
(empty? [1 2])
;; => false
(contains? [1 2 3 4 5] 3)
;; => true (contains? operates on index for vectors)
(contains? [1 2 3 4 5] 6)
;; => false (contains? operates on index for vectors)
(some #(= % 3) [1 2 3 4 5]) ;; alternative for checking if a vector contains a value
;; => true
(some #(= % 6) [1 2 3 4 5])
;; => nil
(every? (fn [item] (> item 3)) [3 4 5 6])
;; => false
(every? (fn [item] (> item 1)) [3 4 5 6])
;; => true
(every? #(even? %) [2 4 6 8])
;; => true
(every? #(odd? %) [2 4 6 8])
;; => false
(some (fn [item] (> item 3)) [3 4 5 6])
;; => true
(some (fn [item] (> item 10)) [3 4 5 6])
;; => nil
Explanation
This Clojure code mainly demonstrates the use of various functions that operate on collections, specifically vectors. Here is the explanation for each of the expressions:
(ns collections-vectors.core)
- This line is declaring the namespace for this section of code. The namespace in this case iscollections-vectors.core
.(empty? [])
- This function call checks if a vector is empty. The vector passed in here is empty, so it returnstrue
.(empty? [1 2])
- This function call also checks if a vector is empty. The vector passed in here contains elements (1 and 2), so it returnsfalse
.(contains? [1 2 3 4 5] 3)
- This function checks if the index 3 is present in the vector. In this case, the vector does have an index 3, so it returnstrue
.(contains? [1 2 3 4 5] 6)
- This function call is similar to the previous one but checks for index 6 in the vector. Since the vector only has indices up to 4, it returnsfalse
.(some #(= % 3) [1 2 3 4 5])
- This line is checking if any element in the vector is equal to 3. It returnstrue
because 3 is indeed an element in the vector.(some #(= % 6) [1 2 3 4 5])
- This line is similar to the previous one, but checks if any element in the vector is equal to 6. It returnsnil
because 6 is not in the vector.(every? (fn [item] (> item 3)) [3 4 5 6])
- This function call checks if every item in the vector is greater than 3. Since 3 is not greater than 3, it returnsfalse
.(every? (fn [item] (> item 1)) [3 4 5 6])
- This line checks if every item in the vector is greater than 1. Since all elements are indeed greater than 1, it returnstrue
.(every? #(even? %) [2 4 6 8])
- This line checks if every item in the vector is even. All the elements are even, so it returnstrue
.(every? #(odd? %) [2 4 6 8])
- This line checks if every item in the vector is odd. Since none of the elements are odd, it returnsfalse
.(some (fn [item] (> item 3)) [3 4 5 6])
- This line checks if any item in the vector is greater than 3. Since 4, 5, and 6 are greater than 3, it returnstrue
.(some (fn [item] (> item 10)) [3 4 5 6])
- This line checks if any item in the vector is greater than 10. Since none of the elements are greater than 10, it returnsnil
.
Utility Functions
Code
(ns collections-vectors.core)
;; ---
;; Utily functions
;; ---
(count [1 2 3 4 5])
;; => 5
(count [])
;; => 0
(reverse [1 2 3 4 5])
;; => (5 4 3 2 1)
(reverse [])
;; => ()
(sort [5 3 1 2 4])
;; => (1 2 3 4 5)
(sort [])
;; => ()
(distinct [1 2 3 4 5 1 2 3 4 5])
;; => (1 2 3 4 5)
(distinct [1 2 3 4 5])
;; => (1 2 3 4 5)
(distinct [])
;; => ()
(partition 2 [1 2 3 4 5 6])
;; => ((1 2) (3 4) (5 6))
(partition 2 [1 2 3 4 5])
;; => ((1 2) (3 4))
(partition 2 [])
;; => ()
(take 2 [1 2 3 4 5])
;; => (1 2)
(take 3 [1 2 3 4 5])
;; => (1 2 3)
(take 6 [1 2 3 4 5])
;; => (1 2 3 4 5)
(take 6 [])
;; => ()
Explanation
This Clojure code demonstrates the use of a few collection manipulation functions, primarily working with vectors.
count
: This function returns the number of items in a collection. For instance,(count [1 2 3 4 5])
will return 5, because there are 5 elements in the vector. If you applycount
on an empty vector, as in(count [])
, you get 0.reverse
: This function returns a sequence of the items in a collection in the reverse order. So,(reverse [1 2 3 4 5])
will return a sequence(5 4 3 2 1)
. For an empty vector(reverse [])
, it returns an empty list()
.sort
: This function returns a sorted sequence of the items in a collection. The example(sort [5 3 1 2 4])
sorts the numbers in ascending order to yield(1 2 3 4 5)
. Sorting an empty vector with(sort [])
simply returns an empty list.distinct
: This function returns a sequence of the unique items in a collection, in the order they first appear. So,(distinct [1 2 3 4 5 1 2 3 4 5])
will return(1 2 3 4 5)
, eliminating the duplicates. The function works the same way even if the vector has no duplicates, as in(distinct [1 2 3 4 5])
. When called with an empty vector,(distinct [])
, it returns an empty list.partition
: This function splits a collection into chunks of a specified size.(partition 2 [1 2 3 4 5 6])
breaks the original vector into pairs, yielding((1 2) (3 4) (5 6))
. If the collection’s size isn’t a multiple of the partition size, the remaining items are simply dropped, as shown in(partition 2 [1 2 3 4 5])
. Partitioning an empty vector returns an empty list.take
: This function returns the first N items of a collection. For instance,(take 2 [1 2 3 4 5])
will return the first two elements(1 2)
,(take 3 [1 2 3 4 5])
will return the first three(1 2 3)
. If you ask for more items than there are in the collection, as in(take 6 [1 2 3 4 5])
,take
will simply return all items. If you usetake
with an empty vector, it will return an empty list.
Remember that these functions return new sequences and do not modify the original collection. Clojure favors immutability and persistent data structures, fundamental concepts in functional programming.
Conversion to Other Collections
Code
(ns collections-vectors.core)
;; ---
;; Conversion to other collections
;; ---
(apply list [1 2 3 4 5])
;; => (1 2 3 4 5)
(set [1 2 3 4 5])
;; => #{1 2 3 4 5}
(seq [1 2 3 4 5])
;; => (1 2 3 4 5)
;; convert clojure vector to hash-map
(into {} [{:a 2} {:b 4} {:c 6}])
;; => {:a 2, :b 4, :c 6}
(into {} [[:a 2] [:b 4] [:c 6]])
;; => {:a 2, :b 4, :c 6}
(apply str [1 2 3 4 5])
;; => "12345"
(str [1 2 3 4 5])
;; => "[1 2 3 4 5]"
Explanation
This Clojure code demonstrates how to convert a vector to other collection types such as lists, sets, sequences, hash-maps, and strings.
Here is a detailed breakdown of each section:
(apply list [1 2 3 4 5])
: Theapply
function is used to call thelist
function, with the arguments being the elements of the vector. This results in the conversion of the vector to a list.(set [1 2 3 4 5])
: Theset
function converts the vector to a set, removing any duplicates in the process. In this case, the vector does not contain any duplicate values.(seq [1 2 3 4 5])
: Theseq
function converts the vector to a sequence.(into {} [{:a 2} {:b 4} {:c 6}])
: Theinto
function is used to merge the contents of a vector of hash-maps into a single hash-map. Each map in the vector is essentially being concatenated into one map.(into {} [[:a 2] [:b 4] [:c 6]])
: Theinto
function can also be used with a vector of vectors. Each subvector is treated as a key-value pair and the function results in a hash-map.(apply str [1 2 3 4 5])
: Similar to the first example,apply
is used to call thestr
function with the vector elements as arguments. This effectively concatenates the elements of the vector into a string.(str [1 2 3 4 5])
: Unlike the previous example, thestr
function is called directly on the vector. This returns a string representation of the vector, including the square brackets.