Skip to content

Commit

Permalink
Merge pull request #61 from invenia/rf/properties
Browse files Browse the repository at this point in the history
Added properties and propertyvalues iterators
  • Loading branch information
iamed2 authored Jul 3, 2019
2 parents acb368c + 6e3619a commit a2e4d09
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 2 deletions.
16 changes: 16 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,19 @@ Helper macro for returning from the enclosing block when there are no more eleme
```@docs
IterTools.@ifsomething
```

## properties(x)

Iterate over struct or named tuple properties.

```@docs
properties
```

## propertyvalues(x)

Iterate over struct or named tuple property values.

```@docs
propertyvalues
```
67 changes: 66 additions & 1 deletion src/IterTools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export
ncycle,
ivec,
flagfirst,
takewhile
takewhile,
properties,
propertyvalues

function has_length(it)
it_size = IteratorSize(it)
Expand Down Expand Up @@ -908,4 +910,67 @@ Base.IteratorSize(it::TakeWhile) = Base.SizeUnknown()
eltype(::Type{TakeWhile{I}}) where {I} = eltype(I)
IteratorEltype(::Type{TakeWhile{I}}) where {I} = IteratorEltype(I)

struct Properties{T}
x::T
n::Int
names
end

"""
properties(x)
Iterate through the names and value of the properties of `x`.
```jldoctest
julia> collect(properties(1 + 2im))
2-element Array{Any,1}:
(:re, 1)
(:im, 2)
```
"""
function properties(x::T) where T
names = propertynames(x)
return Properties{T}(x, length(names), names)
end

function iterate(p::Properties, state=1)
state > length(p) && return nothing

name = p.names[state]
return ((name, getproperty(p.x, name)), state + 1)
end

struct PropertyValues{T}
x::T
n::Int
names
end

"""
propertyvalues(x)
Iterate through the values of the properties of `x`.
```jldoctest
julia> collect(propertyvalues(1 + 2im))
2-element Array{Any,1}:
1
2
```
"""
function propertyvalues(x::T) where T
names = propertynames(x)
return PropertyValues{T}(x, length(names), names)
end

function iterate(p::PropertyValues, state=1)
state > length(p) && return nothing

name = p.names[state]
return (getproperty(p.x, name), state + 1)
end

length(p::Union{Properties, PropertyValues}) = p.n
IteratorSize(::Type{<:Union{Properties, PropertyValues}}) = HasLength()

end # module IterTools
43 changes: 42 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,53 @@ include("testing_macros.jl")

@test collect(flagfirst(Int[])) == Tuple{Bool,Int}[]
end

@testset "takewhile" begin
@test collect(takewhile(x -> x^2 < 10, 1:10)) == Any[1, 2, 3]
@test collect(takewhile(x -> x^2 < 10, Iterators.countfrom(1))) == Any[1, 2, 3]
@test collect(takewhile(x -> x^2 < 10, 5:10)) == Any[]
@test collect(takewhile(x -> true, 5:10)) == collect(5:10)
end

@testset "properties" begin
p1 = properties(1 + 2im)
@test IteratorEltype(p1) isa HasEltype
@test eltype(p1) == Any
@test IteratorSize(p1) isa HasLength
@test length(p1) == 2
@test collect(p1) == Any[(:re, 1), (:im, 2)]

ntp = (a = "", b = 1, c = 2.0)
p2 = properties(ntp)
@test collect(p2) == Tuple.(collect(pairs(ntp)))

# HasLength used as an example no-field struct
p3 = properties(HasLength())
@test collect(p3) == Any[]
end

@testset "propertyvalues" begin
pv1 = propertyvalues(1 + 2im)
@test IteratorEltype(pv1) isa HasEltype
@test eltype(pv1) == Any
@test IteratorSize(pv1) isa HasLength
@test length(pv1) == 2
@test collect(pv1) == Any[1, 2]

tp = ("", 1, 2.0)
pv2 = propertyvalues(tp)

# getproperty for tuples wasn't introduced until 1.2
# https://github.com/JuliaLang/julia/pull/31324
@static if VERSION < v"1.2.0-DEV.460"
@test_broken collect(pv2) == collect(tp)
else
@test collect(pv2) == collect(tp)
end

# HasLength used as an example no-field struct
pv3 = propertyvalues(HasLength())
@test collect(pv3) == Any[]
end
end
end

0 comments on commit a2e4d09

Please sign in to comment.