# Zipping Julia arrays together

`ZippedArrays`

is a Julia package to zip several (abstract) arrays
together for accessing their elements simultaneously. For instance, assuming
that `A`

, `B`

and `C`

are 3 Julia arrays, then:

```
using ZippedArrays
Z = ZippedArray(A,B,C)
```

builds a zipped array instance `Z`

such that the syntax `Z[i]`

yields the
3-tuple `(A[i],B[i],C[i])`

while the syntax `Z[i] = (a,b,c)`

is equivalent to
`(A[i],B[i],C[i]) = (a,b,c)`

.

Any number of arrays can be zipped together, they must however have the same
indices (as returned by the `axes`

method).

To build an uninitialized zipped array of size `dims`

whose elements are tuples
of items of types `T1`

, `T2`

, etc., call:

`Z = ZippedArray{Tuple{T1,T2,...}}(undef, dims)`

For example:

`Z = ZippedArray{Tuple{Int,Float64}}(undef, 2, 3, 4)`

builds a 3-dimensional array of size `(2,3,4)`

and whose elements are 2-tuples
of type `Tuple{Int,Float64}`

.

Compared to the `zip`

function which only provides means to iterate through its
arguments, a zipped array can be accessed in random order and for reading and
writing. This makes zipped arrays useful for multi-key sorting. For instance:

```
sort!(ZippedArray(A,B);
lt = (x,y) -> ifelse(x[1] == y[1], x[2] < y[2], x[1] < y[1]))
```

will sort in-place vectors `A`

and `B`

such that the values in `A`

are in
increasing order and, in case of equality, the values in `B`

are in increasing
order.

A zipped array is a simple immutable structure wrapped around the arguments of
`ZippedArray`

so zipped arrays are almost costless to build. Below is an
example of how to build an array `C`

whose elements are pairs of values from
`A`

and `B`

and a zipped array `Z`

also built from `A`

and `B`

:

```
using ZippedArrays
n = 10_000
A = rand(Float64, n)
B = rand(Int64, n)
C = [(A[i],B[i]) for i in 1:n]
Z = ZippedArray(A,B)
C == Z # yields true
```

The comparison `C == Z`

shows that the two arrays are virtually the same
(although not the same object, that is `C !== Z`

). Building `Z`

however
requires no copy of array elements and hardly requires additional memory, the
sizes of `Z`

and `C`

are indeed quite different:

```
julia> sizeof(Z)
16
julia> sizeof(C)
160000
```

These numbers may depend on the architecture (here a 64-bit processor).

Thanks to the in-lining of functions and optimizations, a zipped array may also
be faster. For instance, with the arrays `C`

and `Z`

defined above:

```
using BenchmarkTools
function sum_first(A::AbstractArray{<:Tuple})
s = 0.0
@inbounds @simd for i in eachindex(A)
s += first(A[i])
end
return s
end
@btime sum_first($C) # 1.615 μs (0 allocations: 0 bytes)
@btime sum_first($Z) # 643.983 ns (0 allocations: 0 bytes)
```