Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added properties and propertyvalues iterators #61

Merged
merged 2 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
rofinn marked this conversation as resolved.
Show resolved Hide resolved

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
rofinn marked this conversation as resolved.
Show resolved Hide resolved
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