Skip to content

Commit 9e81003

Browse files
unzip: the inverse of zip
1 parent 2b5faef commit 9e81003

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

base/Base.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ include("ntuple.jl")
107107
include("abstractdict.jl")
108108

109109
include("iterators.jl")
110-
using .Iterators: zip, enumerate, only
110+
using .Iterators: zip, unzip, enumerate, only
111111
using .Iterators: Flatten, Filter, product # for generators
112112

113113
include("namedtuple.jl")

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ export
628628

629629
enumerate, # re-exported from Iterators
630630
zip,
631+
unzip,
631632
only,
632633

633634
# object identity and equality

base/iterators.jl

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import .Base:
2222
getindex, setindex!, get, iterate,
2323
popfirst!, isdone, peek
2424

25-
export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, partition
25+
export enumerate, zip, unzip,
26+
rest, countfrom, take, drop, takewhile, dropwhile,
27+
cycle, repeated, product, flatten, partition
2628

2729
tail_if_any(::Tuple{}) = ()
2830
tail_if_any(x::Tuple) = tail(x)
@@ -390,6 +392,68 @@ _zip_iterator_eltype(::Type{Tuple{}}) = HasEltype()
390392

391393
reverse(z::Zip) = Zip(map(reverse, z.is))
392394

395+
# unzip
396+
397+
"""
398+
unzip(itrs) -> Vector{<:Vector}
399+
400+
The `unzip` function takes an iterator of iterators and returns a vector of
401+
vectors such that the first vector contains the first element yielded by each
402+
iterator, the second vector the second element yielded by each iterator, etc.
403+
`unzip` is sort of an inverse to the `zip` operation, as the name suggests.
404+
In particular, if we define
405+
406+
≐(a, b) = collect(collect.(a)) == collect(collect.(b))
407+
408+
then the following identities relating `zip` and `unzip` hold for any `itrs`
409+
that is is an iterator of iterators:
410+
411+
unzip(zip(itrs...)) ≐ itrs
412+
zip(unzip(itrs)...) ≐ itrs
413+
414+
Note that `unzip` does not return an iterator: it always consumes all of
415+
its argument and all of each iterator yielded by its argument. It is only
416+
associated with iteration because it is the inverse of `zip`.
417+
418+
# Examples
419+
420+
```jldoctest
421+
julia> unzip(enumerate("Hello"))
422+
2-element Array{Array{T,1} where T,1}:
423+
[1, 2, 3]
424+
['a', 'b', 'c']
425+
426+
julia> unzip([[1, 'a'], [2.5, 'z'], [0, 'x']])
427+
2-element Array{Array{T,1} where T,1}:
428+
Real[1, 2.5, 0]
429+
['a', 'z', 'x']
430+
```
431+
"""
432+
function unzip(itrs)
433+
n = Base.haslength(itrs) ? length(itrs) : -1
434+
vecs = Vector[]
435+
for itr in itrs
436+
for (j, x) in enumerate(itr)
437+
if length(vecs) < j
438+
v = [x]
439+
push!(vecs, v)
440+
n 0 && sizehint!(v, n)
441+
else
442+
v = vecs[j]
443+
if !(x isa eltype(v))
444+
T = Base.promote_typejoin(typeof(x), eltype(v))
445+
v = vecs[j] = copyto!(similar(v, T), v)
446+
n 0 && sizehint!(v, n)
447+
end
448+
push!(v, x)
449+
end
450+
end
451+
length(first(vecs)) == length(last(vecs)) ||
452+
throw(ArgumentError("unzip called with uneven iterators"))
453+
end
454+
return vecs
455+
end
456+
393457
# filter
394458

395459
struct Filter{F,I}

0 commit comments

Comments
 (0)