Skip to content

Commit e4bdcac

Browse files
unzip: the inverse of zip
1 parent e0bdc76 commit e4bdcac

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

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: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import .Base:
2222
getindex, setindex!, get, iterate,
2323
popfirst!, isdone, peek
2424

25-
export enumerate, zip, rest, countfrom, take, drop, cycle, repeated, product, flatten, partition
25+
export enumerate, zip, unzip, rest, countfrom, take, drop,
26+
cycle, repeated, product, flatten, partition
2627

2728
tail_if_any(::Tuple{}) = ()
2829
tail_if_any(x::Tuple) = tail(x)
@@ -390,6 +391,68 @@ _zip_iterator_eltype(::Type{Tuple{}}) = HasEltype()
390391

391392
reverse(z::Zip) = Zip(map(reverse, z.is))
392393

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

395458
struct Filter{F,I}

0 commit comments

Comments
 (0)