A.Vector (Aja v0.4.5) View Source
A Clojure-like persistent vector with efficient appends and random access.
Persistent vectors
are an efficient alternative to lists.
Many operations for A.Vector
run in effective constant time (length, random access, appends...),
unlike linked lists for which most operations run in linear time.
Functions that need to go through the whole collection like map/2
or foldl/3
are as often fast as
their list equivalents, or sometimes even slightly faster.
Vectors also use less memory than lists for "big" collections (see the Memory usage section).
Make sure to read the Efficiency guide section to get the best performance out of vectors.
Erlang's :array
module offer similar functionalities.
However A.Vector
:
- is a better Elixir citizen: pipe-friendliness,
Access
behaviour,Enum
/Inspect
/Collectable
protocols - should have higher performance in most use cases, especially "loops" like
map/2
/to_list/1
/foldl/3
- mirrors the
Enum
module API, with highly optimized versions for vectors (join/1
,sum/1
,random/1
...) - supports negative indexing (e.g.
-1
corresponds to the last element) - optionally implements the
Jason.Encoder
protocol ifJason
is installed
Note: most of the design is inspired by
this series of blog posts,
but a branching factor of 16 = 2 ^ 4
has been picked instead of 32 = 2 ^ 5
.
This choice was made following performance benchmarking that showed better overall performance
for this particular implementation.
Examples
iex> vector = A.Vector.new(1..10)
#A<vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])>
iex> A.Vector.append(vector, :foo)
#A<vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, :foo])>
iex> vector[3]
4
iex> A.Vector.replace_at(vector, -1, :bar)
#A<vec([1, 2, 3, 4, 5, 6, 7, 8, 9, :bar])>
iex> 3 in vector
true
Access behaviour
A.Vector
implements the Access
behaviour.
iex> vector = A.Vector.new(1..10)
iex> vector[3]
4
iex> put_in(vector[5], :foo)
#A<vec([1, 2, 3, 4, 5, :foo, 7, 8, 9, 10])>
iex> {9, updated} = pop_in(vector[8]); updated
#A<vec([1, 2, 3, 4, 5, 6, 7, 8, 10])>
Convenience vec/1
and vec_size/1
macros
The A.Vector
module can be used without any macro.
The A.vec/1
macro does however provide some syntactic sugar to make
it more convenient to work with vectors of known size, namely:
- pattern match on elements for vectors of known size
- construct new vectors of known size faster, by generating the AST at compile time
Examples:
iex> import A
iex> vec([1, 2, 3])
#A<vec([1, 2, 3])>
iex> vec([1, 2, var, _, _, _]) = A.Vector.new(1..6); var
3
The A.vec_size/1
macro can be used in guards:
iex> import A
iex> match?(v when vec_size(v) > 99, A.Vector.new(1..100))
true
Convenience +++/2
operator
The A.+++/2
operator can make appending to a vector more compact by aliasing A.Vector.concat/2
:
iex> import A
iex> vec([1, 2, 3]) +++ vec([4, 5])
#A<vec([1, 2, 3, 4, 5])>
Pattern-matching and opaque type
An A.Vector
is represented internally using the %A.Vector{}
struct. This struct
can be used whenever there's a need to pattern match on something being an A.Vector
:
iex> match?(%A.Vector{}, A.Vector.new())
true
Note, however, than A.Vector
is an opaque type:
its struct internal fields must not be accessed directly.
As discussed in the previous section, vec/1
makes it
possible to pattern match on size and elements as well as checking the type.
Memory usage
Vectors have a small overhead over lists for smaller collections, but are using far less memory for bigger collections:
iex> memory_for = fn n -> [Enum.to_list(1..n), A.Vector.new(1..n)] |> Enum.map(&:erts_debug.size/1) end
iex> memory_for.(1)
[2, 28]
iex> memory_for.(10)
[20, 28]
iex> memory_for.(100)
[200, 150]
iex> memory_for.(10_000)
[20000, 11370]
If you need to work with vectors containing mostly the same value, A.Vector.duplicate/2
is highly efficient both in time and memory (logarithmic).
It minimizes the number of actual copies and reuses the same nested structures under the hood:
iex> A.Vector.duplicate(0, 10_000) |> :erts_debug.size()
116
iex> A.Vector.duplicate(0, 10_000) |> :erts_debug.flat_size() # when shared over processes / ETS
11370
Even a 1B x 1B matrix of the same element costs virtually nothing!
big_n = 1_000_000_000
0 |> A.Vector.duplicate(big_n) |> A.Vector.duplicate(big_n) |> :erts_debug.size()
538
Efficiency guide
If you are using vectors and not lists, chances are that you care about performance. Here are a couple notes about how to use vectors in an optimal way. Most functions from this module are highly efficient, those that are not will indicate it in their documentation.
But remember the golden rule: in case of doubt, always benchmark.
Avoid prepending
Appending is very efficient, but prepending is highly inefficient since the whole array needs to be reconstructed.
DON'T
A.Vector.prepend(vector, :foo)
DO
[:foo | list] # use lists
A.Vector.append(vector, :foo)
Avoid deletions
This implementation of persistent vectors has many advantages, but it does
not support efficient deletion, with the exception of the last element that
can be popped very efficiently (A.Vector.pop_last/1
, A.Vector.delete_last/1
).
Deleting close to the end of the vector is still fairly fast, but deleting near the beginning needs to reconstruct most of the vector.
Deletion functionality is provided through functions like A.Vector.pop_at/3
and A.Vector.delete_at/2
for the sake of completion, but please note that they
are inefficient and their usage is discouraged.
If you need to be able to pop arbitrary indexes, chances are you should consider
an alternative data structure.
Another possibility could be to use sparse arrays, defining nil
as a deleted value
(but then the indexing and size won't reflect this).
DON'T
A.Vector.pop_at(vector, 3)
A.Vector.delete_at(vector, 3)
pop_in(vector[3])
DO
A.Vector.pop_last(vector)
A.Vector.delete_last(vector)
A.Vector.delete_at(vector, -3) # close to the end
A.Vector.replace_at(vector, 3, nil)
Successive appends
If you just need to append all elements of an enumerable, it is more efficient to use
A.Vector.concat/2
or its alias A.+++/2
than successive calls to A.Vector.append/2
:
DON'T
Enum.reduce(enumerable, vector, fn val, acc -> A.Vector.append(acc, val) end)
Enum.into(enumerable, vector)
DO
A.Vector.concat(vector, enumerable)
vector +++ enumerable
Prefer A.Vector
to Enum
for vectors
Many functions provided in this module are very efficient and should be
used over Enum
functions whenever possible, even if A.Vector
implements
the Enumerable
and Collectable
protocols for convienience:
DON'T
Enum.sum(vector)
Enum.to_list(vector)
Enum.reduce(vector, [], fun)
Enum.into(enumerable, %A.Vector.new())
Enum.into(enumerable, vector)
DO
A.Vector.sum(vector)
A.Vector.to_list(vector)
A.Vector.foldl(vector, [], fun)
A.Vector.new(enumerable)
A.Vector.concat(vector, enumerable)
for
comprehensions are actually using Enumerable
as well, so
the same advice holds:
DON'T
for value <- vector do
do_stuff()
end
DO
for value <- A.Vector.to_list(vector) do
do_stuff()
end
Exceptions: Enum
optimized functions
Enum.member?/2
is implemented in an efficient way, so in/2
is optimal:
DO
33 in vector
Enum.slice/2
and Enum.slice/3
are optimized and their use is encouraged,
other "slicing" functions like Enum.take/2
or Enum.drop/2
however are inefficient:
DON'T
Enum.take(vector, 10)
Enum.drop(vector, 25)
DO
Enum.slice(vector, 0, 10)
Enum.slice(vector, 0..10)
Enum.slice(vector, 25..-1)
Slicing optimization
Slicing any subset on the left on the vector using methods from A.Vector
is
extremely efficient as the vector internals can be reused:
DO
A.Vector.take(vector, 10) # take a positive amount
A.Vector.drop(vector, -20) # drop a negative amount
A.Vector.slice(vector, 0, 10) # slicing from 0
A.Vector.slice(vector, 0..-5) # slicing from 0
A.Vector
and Enum
APIs
Not all Enum
functions have been mirrored in A.Vector
, but
you can try either to:
- use
A.Vector.foldl/3
orA.Vector.foldr/3
to implement it (the latter is better to build lists) - call
A.Vector.to_list/1
before usingEnum
Also, it is worth noting that several A.Vector
functions return vectors,
not lists like their Enum
counterpart:
iex> vector = A.Vector.new(1..10)
iex> A.Vector.map(vector, & (&1 * 7))
#A<vec([7, 14, 21, 28, 35, 42, 49, 56, 63, 70])>
iex> A.Vector.reverse(vector)
#A<vec([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])>
Additional notes
If you need to work with vectors containing mostly the same value, use
A.Vector.duplicate/2
(more details in the Memory usage section).If you work with functions returning vectors of known size, you can use the
A.vec/1
macro to defer the generation of the AST for the internal structure to compile time instead of runtime.A.Vector.new([a, 1, 2, 3, 4]) # structure created at runtime vec([a, 1, 2, 3, 4]) # structure AST defined at compile time
Link to this section Summary
Functions
Returns true
if all elements in enumerable
are truthy.
Returns true
if fun.(element)
is truthy for all elements in enumerable
.
Returns true
if at least one element in enumerable
is truthy.
Returns true
if fun.(element)
is truthy for at least one element in enumerable
.
Appends a value
at the end of a vector
.
See A.Vector.concat/2
.
Finds the element at the given index
(zero-based).
Finds the element at the given index
(zero-based).
Appends all values from an enumerable
at the end of a vector
.
(Inefficient) Returns a copy of vector
without the value at the specified index
.
(Inefficient) Returns a copy of vector
without the value at the specified index
.
Removes the last value from the vector
and returns the updated vector.
Removes the last value from the vector
and returns the updated vector.
Drops the amount of elements from the vector
.
Duplicates the given element n
times in a vector.
Invokes the given fun
for each element in the vector
.
Finds the element at the given index
(zero-based), and returns it in a ok-entry.
If the index
does not exist, returns :error
.
Filters the vector
, i.e. return a new vector containing only elements
for which fun
returns a truthy (neither false
nor nil
) value.
Returns the first element of vector
for which fun
returns a truthy value (neither nil
nor false
).
Similar to find/3
, but returns the value of the function invocation instead of the element itself.
Returns the first element in the vector
or default
if vector
is empty.
Folds (reduces) the given vector
from the left with the function fun
.
Requires an accumulator acc
.
Folds (reduces) the given vector
from the right with the function fun
.
Requires an accumulator acc
.
Gets the value from key and updates it, all in one pass.
Intersperses separator
between each element of the vector
.
Joins the given vector
into a string using joiner
as a separator.
Returns the last element in the vector
or default
if vector
is empty.
Returns a new vector where each element is the result of invoking fun
on each corresponding element of vector
.
Maps and intersperses the vector
in one pass.
Maps and joins the given vector
into a string using joiner
as a separator.
Returns the maximal element in the vector
according to Erlang's term ordering.
Returns the minimal element in the vector
according to Erlang's term ordering.
Returns a new empty vector.
Creates a vector from an enumerable
.
Creates a vector from an enumerable
via the given transform
function.
(Inefficient) Returns and removes the value at the specified index
in the vector
.
(Inefficient) Returns and removes the value at the specified index
in the vector
.
Removes the last value from the vector
and returns both the value and the updated vector.
Removes the last value from the vector
and returns both the value and the updated vector.
(Inefficient) Prepends value
at the beginning of the vector
.
Returns the product of all elements in the vector
.
Returns a random element of a vector
.
Filters the vector
, i.e. return a new vector containing only elements
for which fun
returns a falsy (either false
or nil
) value.
Populates a vector of size n
by calling generator_fun
repeatedly.
Returns a copy of vector
with a replaced value
at the specified index
.
Returns a copy of vector
with a replaced value
at the specified index
.
Returns the vector
in reverse order.
Returns a new vector with the elements of vector
shuffled.
Returns the number of elements in vector
.
Returns a subset of the given vector
by index_range
.
Returns a subset of the given vector
, from start_index
(zero-based)
with amount number
of elements if available.
Sorts the vector
in the same way as Enum.sort/1
.
Sorts the vector
in the same way as Enum.sort/2
.
Sorts the vector
in the same way as Enum.sort_by/3
.
Returns the sum of all elements in the vector
.
Takes an amount
of elements from the beginning or the end of the vector
.
Takes amount
random elements from vector
.
Converts the vector
to a list.
Returns a copy of the vector without any duplicated element.
Returns a copy of the vector without elements for which the function fun
returned duplicate elements.
Returns a copy of vector
with an updated value at the specified index
.
Returns a copy of vector
with an updated value at the specified index
.
Returns the vector
with each element wrapped in a tuple alongside its index.
Link to this section Types
Link to this section Functions
Specs
Returns true
if all elements in enumerable
are truthy.
Runs in linear time, but stops evaluating when finds the first falsy value.
Iterates over the enumerable
, and when it finds a falsy value (false
or nil
),
false
is returned. In all other cases true
is returned.
Examples
iex> A.Vector.new([true, true, false]) |> A.Vector.all?()
false
iex> A.Vector.new([true, [], %{}, 5]) |> A.Vector.all?()
true
iex> A.Vector.new() |> A.Vector.all?()
true
Specs
all?(t(val), (val -> as_boolean(term()))) :: boolean() when val: value()
Returns true
if fun.(element)
is truthy for all elements in enumerable
.
Runs in linear time, but stops evaluating when finds the first falsy value.
Iterates over the enumerable
and invokes fun
on each element. When an invocation
of fun
returns a falsy value (false
or nil
) iteration stops immediately and
false
is returned. In all other cases true
is returned.
Examples
iex> vector = A.Vector.new(1..10)
iex> A.Vector.all?(vector, fn i -> rem(i, 13) != 0 end)
true
iex> A.Vector.all?(vector, fn i -> rem(i, 7) != 0 end)
false
iex> A.Vector.new() |> A.Vector.all?(fn i -> rem(i, 7) != 0 end)
true
Specs
Returns true
if at least one element in enumerable
is truthy.
Runs in linear time, but stops evaluating when finds the first truthy value.
Iterates over the enumerable
, and when it finds a truthy value
(neither false
nor nil
), true
is returned.
In all other cases false
is returned.
Examples
iex> A.Vector.new([false, false, true]) |> A.Vector.any?()
true
iex> A.Vector.new([false, nil]) |> A.Vector.any?()
false
iex> A.Vector.new() |> A.Vector.any?()
false
Specs
any?(t(val), (val -> as_boolean(term()))) :: boolean() when val: value()
Returns true
if fun.(element)
is truthy for at least one element in enumerable
.
Runs in linear time, but stops evaluating when finds the first truthy value.
Iterates over the enumerable
and invokes fun
on each element. When an invocation
of fun
returns a truthy value (neither false
nor nil
) iteration stops immediately
and true
is returned. In all other cases false
is returned.
Examples
iex> vector = A.Vector.new(1..10)
iex> A.Vector.any?(vector, fn i -> rem(i, 7) == 0 end)
true
iex> A.Vector.any?(vector, fn i -> rem(i, 13) == 0 end)
false
iex> A.Vector.new() |> A.Vector.any?(fn i -> rem(i, 7) == 0 end)
false
Specs
Appends a value
at the end of a vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new() |> A.Vector.append(:foo)
#A<vec([:foo])>
iex> A.Vector.new(1..5) |> A.Vector.append(:foo)
#A<vec([1, 2, 3, 4, 5, :foo])>
See A.Vector.concat/2
.
Specs
Finds the element at the given index
(zero-based).
Returns default
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..1_000) |> A.Vector.at(555)
556
iex> A.Vector.new(1..1_000) |> A.Vector.at(1_000)
nil
Specs
Specs
Finds the element at the given index
(zero-based).
Raises an A.Vector.IndexError
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..1_000) |> A.Vector.at!(555)
556
iex> A.Vector.new(1..1_000) |> A.Vector.at!(-10)
991
iex> A.Vector.new(1..1_000) |> A.Vector.at!(1_000)
** (A.Vector.IndexError) out of bound index: 1000 not in -1000..999
Specs
concat(t(val), Enumerable.t()) :: t(val) when val: value()
Appends all values from an enumerable
at the end of a vector
.
Runs in effective linear time in respect with the length of enumerable
,
disregarding the size of the vector
.
Examples
iex> A.Vector.new(1..5) |> A.Vector.concat(10..15)
#A<vec([1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15])>
iex> A.Vector.new() |> A.Vector.concat(10..15)
#A<vec([10, 11, 12, 13, 14, 15])>
Specs
(Inefficient) Returns a copy of vector
without the value at the specified index
.
Returns the vector
untouched if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in linear time. Its usage is discouraged, see the Efficiency guide.
Examples
iex> vector = A.Vector.new(1..8)
iex> A.Vector.delete_at(vector, 4)
#A<vec([1, 2, 3, 4, 6, 7, 8])>
iex> A.Vector.delete_at(vector, -9)
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
Specs
(Inefficient) Returns a copy of vector
without the value at the specified index
.
Raises an A.Vector.IndexError
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in linear time. Its usage is discouraged, see the Efficiency guide.
Examples
iex> vector = A.Vector.new(1..8)
iex> A.Vector.delete_at!(vector, 4)
#A<vec([1, 2, 3, 4, 6, 7, 8])>
iex> A.Vector.delete_at!(vector, -9)
** (A.Vector.IndexError) out of bound index: -9 not in -8..7
Specs
Removes the last value from the vector
and returns the updated vector.
Leaves the vector
untouched if empty.
Runs in effective constant time.
Examples
iex> vector = A.Vector.new(1..8)
iex> A.Vector.delete_last(vector)
#A<vec([1, 2, 3, 4, 5, 6, 7])>
iex> A.Vector.delete_last(A.Vector.new())
#A<vec([])>
Specs
Removes the last value from the vector
and returns the updated vector.
Raises an A.Vector.EmptyError
if empty.
Runs in effective constant time.
Examples
iex> vector = A.Vector.new(1..8)
iex> A.Vector.delete_last!(vector)
#A<vec([1, 2, 3, 4, 5, 6, 7])>
iex> A.Vector.delete_last!(A.Vector.new())
** (A.Vector.EmptyError) empty vector error
Specs
Drops the amount of elements from the vector
.
If a negative amount
is given, the amount of last values will be dropped.
Time complexity is:
- linear when
amount
is positive, as the vector needs to be reconstructed. - effective constant time when
amount
is negative, as the vector structure can be shared
Examples
iex> A.Vector.new(0..15) |> A.Vector.drop(10)
#A<vec([10, 11, 12, 13, 14, 15])>
iex> A.Vector.new(0..5) |> A.Vector.drop(0)
#A<vec([0, 1, 2, 3, 4, 5])>
iex> A.Vector.new(0..10) |> A.Vector.drop(-5)
#A<vec([0, 1, 2, 3, 4, 5])>
Specs
duplicate(val, non_neg_integer()) :: t(val) when val: value()
Duplicates the given element n
times in a vector.
n
is an integer greater than or equal to 0
.
If n
is 0
, an empty list is returned.
Runs in logarithmic time regarding n
. It is very fast and memory efficient
(see Memory usage).
Examples
iex> A.Vector.duplicate(nil, 10)
#A<vec([nil, nil, nil, nil, nil, nil, nil, nil, nil, nil])>
iex> A.Vector.duplicate(:foo, 0)
#A<vec([])>
Specs
Invokes the given fun
for each element in the vector
.
Returns :ok
.
Runs in linear time.
Examples
A.Vector.new(1..3) |> A.Vector.each(&IO.inspect/1)
1
2
3
:ok
Specs
Finds the element at the given index
(zero-based), and returns it in a ok-entry.
If the index
does not exist, returns :error
.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..1_000) |> A.Vector.fetch(555)
{:ok, 556}
iex> A.Vector.new(1..1_000) |> A.Vector.fetch(1_000)
:error
iex> A.Vector.new(1..1_000) |> A.Vector.fetch(-1)
{:ok, 1000}
Specs
Filters the vector
, i.e. return a new vector containing only elements
for which fun
returns a truthy (neither false
nor nil
) value.
Runs in linear time.
Examples
iex> vector = A.Vector.new(1..100)
iex> A.Vector.filter(vector, fn i -> rem(i, 13) == 0 end)
#A<vec([13, 26, 39, 52, 65, 78, 91])>
Specs
find(t(val), default, (val -> as_boolean(term()))) :: val | default when val: value(), default: value()
Returns the first element of vector
for which fun
returns a truthy value (neither nil
nor false
).
If no such element is found, returns default
.
Runs in linear time, but stops evaluating when finds the first truthy value.
Examples
iex> vector = A.Vector.new(2..10)
iex> A.Vector.find(vector, fn i -> rem(49, i) == 0 end)
7
iex> A.Vector.find(vector, fn i -> rem(13, i) == 0 end)
nil
Specs
find_value(t(val), default, (val -> new_val)) :: new_val | default when val: value(), new_val: value(), default: value()
Similar to find/3
, but returns the value of the function invocation instead of the element itself.
The return value is considered to be found when the result is truthy (neither nil
nor false
).
Runs in linear time, but stops evaluating when finds the first truthy value.
Examples
iex> vector = A.Vector.new(["Ant", "Bat", "Cat", "Dinosaur"])
iex> A.Vector.find_value(vector, fn s -> String.at(s, 4) end)
"s"
iex> A.Vector.find_value(vector, fn s -> String.at(s, 10) end)
nil
Specs
Returns the first element in the vector
or default
if vector
is empty.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..10_000) |> A.Vector.first()
1
iex> A.Vector.new() |> A.Vector.first()
nil
Specs
Folds (reduces) the given vector
from the left with the function fun
.
Requires an accumulator acc
.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.foldl(0, &+/2)
55
iex> A.Vector.new(1..10) |> A.Vector.foldl([], & [&1 | &2])
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Specs
Folds (reduces) the given vector
from the right with the function fun
.
Requires an accumulator acc
.
Unlike linked lists, this is as efficient as foldl/3
. This can typically save a call
to Enum.reverse/1
on the result when building a list.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.foldr(0, &+/2)
55
iex> A.Vector.new(1..10) |> A.Vector.foldr([], & [&1 | &2])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Specs
get_and_update(t(v), index(), (v -> {returned, v} | :pop)) :: {returned, t(v)} when v: value(), returned: term()
Gets the value from key and updates it, all in one pass.
See Access.get_and_update/3
for more details.
Examples
iex> vector = A.Vector.new(1..8)
iex> {6, updated} = A.Vector.get_and_update(vector, 5, fn current_value ->
...> {current_value, current_value && current_value * 100}
...> end); updated
#A<vec([1, 2, 3, 4, 5, 600, 7, 8])>
iex> {nil, updated} = A.Vector.get_and_update(vector, 8, fn current_value ->
...> {current_value, current_value && current_value * 100}
...> end); updated
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
iex> {4, updated} = A.Vector.get_and_update(vector, 3, fn _ -> :pop end); updated
#A<vec([1, 2, 3, 5, 6, 7, 8])>
iex> {nil, updated} = A.Vector.get_and_update(vector, 8, fn _ -> :pop end); updated
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
Specs
Intersperses separator
between each element of the vector
.
Runs in linear time.
Examples
iex> A.Vector.new(1..6) |> A.Vector.intersperse(nil)
#A<vec([1, nil, 2, nil, 3, nil, 4, nil, 5, nil, 6])>
Specs
join(t(val), String.t()) :: String.t() when val: String.Chars.t()
Joins the given vector
into a string using joiner
as a separator.
If joiner
is not passed at all, it defaults to an empty string.
All elements in the vector
must be convertible to a string, otherwise an error is raised.
Runs in linear time.
Examples
iex> A.Vector.new(1..6) |> A.Vector.join()
"123456"
iex> A.Vector.new(1..6) |> A.Vector.join(" + ")
"1 + 2 + 3 + 4 + 5 + 6"
iex> A.Vector.new() |> A.Vector.join(" + ")
""
Specs
Returns the last element in the vector
or default
if vector
is empty.
Runs in constant time (actual, not effective).
Examples
iex> A.Vector.new(1..10_000) |> A.Vector.last()
10_000
iex> A.Vector.new() |> A.Vector.last()
nil
Specs
Returns a new vector where each element is the result of invoking fun
on each corresponding element of vector
.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.map(&(&1 * &1))
#A<vec([1, 4, 9, 16, 25, 36, 49, 64, 81, 100])>
Specs
map_intersperse(t(val), separator, (val -> mapped_val)) :: t(mapped_val | separator) when val: value(), separator: value(), mapped_val: value()
Maps and intersperses the vector
in one pass.
Runs in linear time.
Examples
iex> A.Vector.new(1..6) |> A.Vector.map_intersperse(nil, &(&1 * 10))
#A<vec([10, nil, 20, nil, 30, nil, 40, nil, 50, nil, 60])>
Specs
map_join(t(val), String.t(), (val -> String.Chars.t())) :: String.t() when val: value()
Maps and joins the given vector
into a string using joiner
as a separator.
If joiner
is not passed at all, it defaults to an empty string.
mapper
should only return values that are convertible to a string, otherwise an error is raised.
Runs in linear time.
Examples
iex> A.Vector.new(1..6) |> A.Vector.map_join(fn x -> x * 10 end)
"102030405060"
iex> A.Vector.new(1..6) |> A.Vector.map_join(" + ", fn x -> x * 10 end)
"10 + 20 + 30 + 40 + 50 + 60"
iex> A.Vector.new() |> A.Vector.map_join(" + ", fn x -> x * 10 end)
""
Specs
Returns the maximal element in the vector
according to Erlang's term ordering.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.max()
10
iex> A.Vector.new() |> A.Vector.max()
** (A.Vector.EmptyError) empty vector error
Specs
Returns the minimal element in the vector
according to Erlang's term ordering.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.min()
1
iex> A.Vector.new() |> A.Vector.min()
** (A.Vector.EmptyError) empty vector error
Specs
new() :: t()
Returns a new empty vector.
Examples
iex> A.Vector.new()
#A<vec([])>
Specs
new(Enumerable.t()) :: t()
Creates a vector from an enumerable
.
Runs in linear time.
Examples
iex> A.Vector.new(10..25)
#A<vec([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25])>
Specs
new(Enumerable.t(), (v1 -> v2)) :: t(v2) when v1: value(), v2: value()
Creates a vector from an enumerable
via the given transform
function.
Examples
iex> A.Vector.new(1..10, &(&1 * &1))
#A<vec([1, 4, 9, 16, 25, 36, 49, 64, 81, 100])>
Specs
(Inefficient) Returns and removes the value at the specified index
in the vector
.
Returns the vector
untouched if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in linear time. Its usage is discouraged, see the Efficiency guide.
Examples
iex> vector = A.Vector.new(1..8)
iex> {5, updated} = A.Vector.pop_at(vector, 4); updated
#A<vec([1, 2, 3, 4, 6, 7, 8])>
iex> {nil, updated} = A.Vector.pop_at(vector, -9); updated
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
Specs
(Inefficient) Returns and removes the value at the specified index
in the vector
.
Raises an A.Vector.IndexError
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in linear time. Its usage is discouraged, see the Efficiency guide.
Examples
iex> vector = A.Vector.new(1..8)
iex> {5, updated} = A.Vector.pop_at!(vector, 4); updated
#A<vec([1, 2, 3, 4, 6, 7, 8])>
iex> A.Vector.pop_at!(vector, -9)
** (A.Vector.IndexError) out of bound index: -9 not in -8..7
Specs
Removes the last value from the vector
and returns both the value and the updated vector.
Leaves the vector
untouched if empty.
Runs in effective constant time.
Examples
iex> vector = A.Vector.new(1..8)
iex> {8, updated} = A.Vector.pop_last(vector); updated
#A<vec([1, 2, 3, 4, 5, 6, 7])>
iex> {nil, updated} = A.Vector.pop_last(A.Vector.new()); updated
#A<vec([])>
Specs
Removes the last value from the vector
and returns both the value and the updated vector.
Raises an A.Vector.EmptyError
if empty.
Runs in effective constant time.
Examples
iex> vector = A.Vector.new(1..8)
iex> {8, updated} = A.Vector.pop_last!(vector); updated
#A<vec([1, 2, 3, 4, 5, 6, 7])>
iex> {nil, updated} = A.Vector.pop_last!(A.Vector.new()); updated
** (A.Vector.EmptyError) empty vector error
Specs
(Inefficient) Prepends value
at the beginning of the vector
.
Runs in linear time because the whole vector needs to be reconstructuded, and should be avoided.
Examples
iex> A.Vector.new() |> A.Vector.prepend(:foo)
#A<vec([:foo])>
iex> A.Vector.new(1..5) |> A.Vector.prepend(:foo)
#A<vec([:foo, 1, 2, 3, 4, 5])>
Specs
Returns the product of all elements in the vector
.
Raises ArithmeticError
if vector
contains a non-numeric value.
Runs in linear time.
Examples
iex> A.Vector.new(1..5) |> A.Vector.product()
120
iex> A.Vector.new() |> A.Vector.product()
1
Specs
Returns a random element of a vector
.
Raises Vector.EmptyError
if vector
is empty.
Like Enum.random/1
, this function uses Erlang's :rand
module
to calculate the random value.
Check its documentation for setting a different random algorithm or a different seed.
Runs in effective constant time, and is therefore more efficient than Enum.random/1
on lists.
Examples
# Although not necessary, let's seed the random algorithm
iex>:rand.seed(:exrop, {101, 102, 103})
iex> A.Vector.new([1, 2, 3]) |> A.Vector.random()
3
iex> A.Vector.new([1, 2, 3]) |> A.Vector.random()
2
iex> A.Vector.new(1..1_000) |> A.Vector.random()
846
iex> A.Vector.new([]) |> A.Vector.random()
** (A.Vector.EmptyError) empty vector error
Specs
Filters the vector
, i.e. return a new vector containing only elements
for which fun
returns a falsy (either false
or nil
) value.
Runs in linear time.
Examples
iex> vector = A.Vector.new(1..12)
iex> A.Vector.reject(vector, fn i -> rem(i, 3) == 0 end)
#A<vec([1, 2, 4, 5, 7, 8, 10, 11])>
Populates a vector of size n
by calling generator_fun
repeatedly.
Examples
# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exsplus, {1, 2, 3})
iex> A.Vector.repeatedly(&:rand.uniform/0, 3)
#A<vec([0.40502929729990744, 0.45336720247823126, 0.04094511692041057])>
Specs
Returns a copy of vector
with a replaced value
at the specified index
.
Returns the vector
untouched if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..8) |> A.Vector.replace_at(5, :foo)
#A<vec([1, 2, 3, 4, 5, :foo, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.replace_at(8, :foo)
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.replace_at(-2, :foo)
#A<vec([1, 2, 3, 4, 5, 6, :foo, 8])>
Specs
Returns a copy of vector
with a replaced value
at the specified index
.
Raises an A.Vector.IndexError
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..8) |> A.Vector.replace_at!(5, :foo)
#A<vec([1, 2, 3, 4, 5, :foo, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.replace_at!(-2, :foo)
#A<vec([1, 2, 3, 4, 5, 6, :foo, 8])>
iex> A.Vector.new(1..8) |> A.Vector.replace_at!(8, :foo)
** (A.Vector.IndexError) out of bound index: 8 not in -8..7
Specs
Returns the vector
in reverse order.
Runs in linear time.
Examples
iex> A.Vector.new(1..12) |> A.Vector.reverse()
#A<vec([12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1])>
Specs
Returns a new vector with the elements of vector
shuffled.
See Enum.shuffle/1
for notes on implementation and random seed.
Examples
# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exrop, {1, 2, 3})
iex> A.Vector.new([1, 2, 3]) |> A.Vector.shuffle()
#A<vec([3, 1, 2])>
iex> A.Vector.new([1, 2, 3]) |> A.Vector.shuffle()
#A<vec([1, 3, 2])>
Specs
size(t()) :: non_neg_integer()
Returns the number of elements in vector
.
Runs in constant time.
Examples
iex> A.Vector.new(10_000..20_000) |> A.Vector.size()
10001
iex> A.Vector.new() |> A.Vector.size()
0
Specs
Returns a subset of the given vector
by index_range
.
Works the same as Enum.slice/2
, see its documentation for more details.
Runs in linear time regarding the size of the returned subset.
Examples
iex> A.Vector.new(0..100) |> A.Vector.slice(80..90)
#A<vec([80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90])>
iex> A.Vector.new(0..100) |> A.Vector.slice(-40..-30)
#A<vec([61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71])>
iex> A.Vector.new([:only_one]) |> A.Vector.slice(0..1000)
#A<vec([:only_one])>
Specs
slice(t(val), index(), non_neg_integer()) :: t(val) when val: value()
Returns a subset of the given vector
, from start_index
(zero-based)
with amount number
of elements if available.
Works the same as Enum.slice/3
, see its documentation for more details.
Runs in linear time regarding the size of the returned subset.
Examples
iex> A.Vector.new(0..100) |> A.Vector.slice(80, 10)
#A<vec([80, 81, 82, 83, 84, 85, 86, 87, 88, 89])>
iex> A.Vector.new(0..100) |> A.Vector.slice(-40, 10)
#A<vec([61, 62, 63, 64, 65, 66, 67, 68, 69, 70])>
iex> A.Vector.new([:only_one]) |> A.Vector.slice(0, 1000)
#A<vec([:only_one])>
Specs
Sorts the vector
in the same way as Enum.sort/1
.
Examples
iex> A.Vector.new(9..1) |> A.Vector.sort()
#A<vec([1, 2, 3, 4, 5, 6, 7, 8, 9])>
Specs
sort( t(val), (val, val -> boolean()) | :asc | :desc | module() | {:asc | :desc, module()} ) :: t(val) when val: value()
Sorts the vector
in the same way as Enum.sort/2
.
See Enum.sort/2
documentation for detailled usage.
Examples
iex> A.Vector.new(1..9) |> A.Vector.sort(:desc)
#A<vec([9, 8, 7, 6, 5, 4, 3, 2, 1])>
Specs
sort_by( t(val), (val -> mapped_val), (val, val -> boolean()) | :asc | :desc | module() | {:asc | :desc, module()} ) :: t(val) when val: value(), mapped_val: value()
Sorts the vector
in the same way as Enum.sort_by/3
.
See Enum.sort_by/3
documentation for detailled usage.
Examples
iex> vector = A.Vector.new(["some", "kind", "of", "monster"])
iex> A.Vector.sort_by(vector, &byte_size/1)
#A<vec(["of", "some", "kind", "monster"])>
iex> A.Vector.sort_by(vector, &{byte_size(&1), String.first(&1)})
#A<vec(["of", "kind", "some", "monster"])>
Specs
Returns the sum of all elements in the vector
.
Raises ArithmeticError
if vector
contains a non-numeric value.
Runs in linear time.
Examples
iex> A.Vector.new(1..10) |> A.Vector.sum()
55
iex> A.Vector.new() |> A.Vector.sum()
0
Specs
Takes an amount
of elements from the beginning or the end of the vector
.
If a positive amount
is given, it takes the amount elements from the beginning of the vector
.
If a negative amount
is given, the amount of elements will be taken from the end.
If amount is 0, it returns the empty vector.
Time complexity is:
- effective constant time when
amount
is positive, as the vector structure can be shared - linear when
amount
is negative, as the vector needs to be reconstructed.
Examples
iex> A.Vector.new(0..100) |> A.Vector.take(10)
#A<vec([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])>
iex> A.Vector.new([:only_one]) |> A.Vector.take(1000)
#A<vec([:only_one])>
iex> A.Vector.new(0..10) |> A.Vector.take(-5)
#A<vec([6, 7, 8, 9, 10])>
Specs
take_random(t(val), non_neg_integer()) :: t(val) when val: value()
Takes amount
random elements from vector
.
Note that, unless amount
is 0
or 1
, this function will
traverse the whole vector
to get the random sub-vector.
If amount
is more than the vector
size, this is equivalent to shuffling the vector
:
the returned vector cannot be bigger than the original one.
See Enum.random/1
for notes on implementation and random seed.
Runs in linerar time (except for amount <= 1
, which is effective constant time).
Examples
# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exrop, {1, 2, 3})
iex> A.Vector.new(1..10) |> A.Vector.take_random(2)
#A<vec([7, 2])>
iex> A.Vector.new([:foo, :bar, :baz]) |> A.Vector.take_random(100)
#A<vec([:bar, :baz, :foo])>
Specs
Converts the vector
to a list.
Runs in linear time.
Examples
iex> A.Vector.new(10..25) |> A.Vector.to_list()
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
iex> A.Vector.new() |> A.Vector.to_list()
[]
Specs
Returns a copy of the vector without any duplicated element.
The first occurrence of each element is kept.
Runs in linear time.
Examples
iex> A.Vector.new([1, 1, 2, 1, 2, 3, 2]) |> A.Vector.uniq()
#A<vec([1, 2, 3])>
Specs
Returns a copy of the vector without elements for which the function fun
returned duplicate elements.
The first occurrence of each element is kept.
Runs in linear time.
Examples
iex> vector = A.Vector.new([x: 1, y: 2, z: 1])
#A<vec([x: 1, y: 2, z: 1])>
iex> A.Vector.uniq_by(vector, fn {_x, y} -> y end)
#A<vec([x: 1, y: 2])>
Specs
Returns a copy of vector
with an updated value at the specified index
.
Returns the vector
untouched if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..8) |> A.Vector.update_at(2, &(&1 * 1000))
#A<vec([1, 2, 3000, 4, 5, 6, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.update_at(8, &(&1 * 1000))
#A<vec([1, 2, 3, 4, 5, 6, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.update_at(-1, &(&1 * 1000))
#A<vec([1, 2, 3, 4, 5, 6, 7, 8000])>
Specs
Returns a copy of vector
with an updated value at the specified index
.
Raises an A.Vector.IndexError
if index
is out of bounds.
Supports negative indexing from the end of the vector
.
Runs in effective constant time.
Examples
iex> A.Vector.new(1..8) |> A.Vector.update_at!(2, &(&1 * 1000))
#A<vec([1, 2, 3000, 4, 5, 6, 7, 8])>
iex> A.Vector.new(1..8) |> A.Vector.update_at!(-1, &(&1 * 1000))
#A<vec([1, 2, 3, 4, 5, 6, 7, 8000])>
iex> A.Vector.new(1..8) |> A.Vector.update_at!(-9, &(&1 * 1000))
** (A.Vector.IndexError) out of bound index: -9 not in -8..7
Specs
Returns the vector
with each element wrapped in a tuple alongside its index.
If an offset
is given, we will index from the given offset
instead of from zero.
Runs in linear time.
Examples
iex> A.Vector.new(["foo", "bar", "baz"]) |> A.Vector.with_index()
#A<vec([{"foo", 0}, {"bar", 1}, {"baz", 2}])>
iex> A.Vector.new() |> A.Vector.with_index()
#A<vec([])>
iex> A.Vector.new(["foo", "bar", "baz"]) |> A.Vector.with_index(100)
#A<vec([{"foo", 100}, {"bar", 101}, {"baz", 102}])>