Skip to content

Commit b4ba06c

Browse files
authored
Add nitems, firstitem(s), and lastitem(s) (#37)
See also: JuliaLang/julia#35947
1 parent f4d899e commit b4ba06c

6 files changed

+209
-2
lines changed

src/DataTools.jl

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
11
module DataTools
22

3-
export averaging, inc1, meanvar, modifying, oncol, rightif
3+
export averaging,
4+
firstitem,
5+
firstitems,
6+
inc1,
7+
lastitem,
8+
lastitems,
9+
meanvar,
10+
modifying,
11+
nitems,
12+
oncol,
13+
rightif
414

15+
using Base: HasLength, HasShape, IteratorSize
516
using InitialValues: InitialValues
617
using Setfield: @lens, Lens, PropertyLens, modify, set
718
using StaticNumbers: static
819
using Statistics: Statistics, mean, std, var
920
using Tables: Tables
10-
using Transducers: Map, Transducers, combine, complete, next, reducingfunction, start
21+
using Transducers:
22+
Composition,
23+
Count,
24+
IdentityTransducer,
25+
Map,
26+
MapSplat,
27+
Scan,
28+
Take,
29+
TakeLast,
30+
Transducers,
31+
combine,
32+
complete,
33+
extract_transducer,
34+
next,
35+
opcompose,
36+
reducingfunction,
37+
right,
38+
start
1139

1240
include("utils.jl")
1341
include("oncol.jl")
1442
include("modifying.jl")
1543
include("reductions.jl")
44+
include("reducers.jl")
1645

1746
# Use README as the docstring of the module:
1847
@doc let path = joinpath(dirname(@__DIR__), "README.md")

src/reducers.jl

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""
2+
nitems(xs) -> n::Integer
3+
4+
Count number of items in `xs`. Consume `xs` if necessary.
5+
6+
# Examples
7+
```jldoctest
8+
julia> using DataTools, Transducers
9+
10+
julia> nitems(1:10)
11+
10
12+
13+
julia> 1:10 |> Filter(isodd) |> Map(inv) |> nitems
14+
5
15+
```
16+
"""
17+
nitems
18+
nitems(xs) =
19+
if IteratorSize(xs) isa Union{HasLength, HasShape}
20+
length(xs)
21+
else
22+
xf, coll = extract_transducer(xs)
23+
_nitems(_pop_innermost_maplikes(xf), coll)
24+
end
25+
26+
_pop_innermost_maplikes(xf) = xf
27+
_pop_innermost_maplikes(::Union{Map,MapSplat,Scan}) = IdentityTransducer()
28+
function _pop_innermost_maplikes(xf::Composition)
29+
inner = _pop_innermost_maplikes(xf.inner)
30+
if inner isa IdentityTransducer
31+
return _pop_innermost_maplikes(xf.outer)
32+
else
33+
opcompose(xf.outer, inner)
34+
end
35+
end
36+
37+
_nitems(::IdentityTransducer, xs) = _nitems(xs)
38+
_nitems(xf, xs) = xs |> xf |> _nitems
39+
40+
_nitems(xs) =
41+
if IteratorSize(xs) isa Union{HasLength, HasShape}
42+
length(xs)
43+
else
44+
foldl(inc1, IdentityTransducer(), xs)
45+
end
46+
# TODO: optimization for `Cat`.
47+
48+
"""
49+
firstitem(xs)
50+
51+
Get the first item of `xs`. Consume `xs` if necessary.
52+
53+
# Examples
54+
```jldoctest
55+
julia> using DataTools, Transducers
56+
57+
julia> firstitem(3:7)
58+
3
59+
60+
julia> 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> firstitem
61+
5
62+
```
63+
"""
64+
firstitem
65+
firstitem(xs::AbstractArray) = first(xs)
66+
firstitem(xs) = foldl(right, Take(1), xs)
67+
68+
"""
69+
lastitem(xs)
70+
71+
Get the last item of `xs`. Consume `xs` if necessary.
72+
73+
# Examples
74+
```jldoctest
75+
julia> using DataTools, Transducers
76+
77+
julia> lastitem(3:7)
78+
7
79+
80+
julia> 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> lastitem
81+
7
82+
```
83+
"""
84+
lastitem
85+
lastitem(xs::AbstractArray) = last(xs)
86+
lastitem(xs) = foldl(right, Map(identity), xs)
87+
88+
"""
89+
firstitems(xs, n::Integer)
90+
firstitems(n::Integer) -> xs -> firstitems(xs, n)
91+
92+
Get the first `n` items of `xs`. Consume `xs` if necessary.
93+
"""
94+
firstitems
95+
firstitems(n::Integer) = xs -> firstitems(xs, n)
96+
firstitems(xs, n::Integer) = collect(Take(n), xs)
97+
firstitems(xs::AbstractArray, n::Integer) = view(xs, firstindex(xs):firstindex(xs)+n-1)
98+
99+
"""
100+
lastitems(xs, n::Integer)
101+
lastitems(n::Integer) -> xs -> lastitems(xs, n)
102+
103+
Get the last `n` items of `xs`. Consume `xs` if necessary.
104+
"""
105+
lastitems
106+
lastitems(n::Integer) = xs -> lastitems(xs, n)
107+
lastitems(xs, n::Integer) = collect(TakeLast(n), xs)
108+
lastitems(xs::AbstractArray, n::Integer) = view(xs, lastindex(xs)-n+1:lastindex(xs))

test/test_firstitems.jl

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module TestFirstitems
2+
3+
using DataTools
4+
using Test
5+
using Transducers
6+
7+
include("utils.jl")
8+
9+
@testset "firstitem" begin
10+
@test firstitem(3:7) === 3
11+
@test 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> firstitem == 5
12+
end
13+
14+
@testset "firstitems" begin
15+
@test firstitems(3:7, 2) ==view(3:7, 1:2)
16+
@test 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> firstitems(2) == [5, 7]
17+
end
18+
19+
end # module

test/test_lastitems.jl

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module TestLastitems
2+
3+
using DataTools
4+
using Test
5+
using Transducers
6+
7+
include("utils.jl")
8+
9+
@testset "lastitem" begin
10+
@test lastitem(3:7) === 7
11+
@test 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> lastitem == 7
12+
end
13+
14+
@testset "lastitems" begin
15+
@test lastitems(3:7, 2) ==view(3:7, 4:5)
16+
@test 3:7 |> Map(x -> x + 1) |> Filter(isodd) |> lastitems(2) == [5, 7]
17+
end
18+
19+
end # module

test/test_nitems.jl

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module TestNItems
2+
3+
using DataTools
4+
using Test
5+
using Transducers
6+
using Transducers: IdentityTransducer
7+
8+
@testset "_pop_innermost_maplikes" begin
9+
pop(args...) = DataTools._pop_innermost_maplikes(opcompose(args...))
10+
@test pop(Map(inv)) === IdentityTransducer()
11+
@test pop(MapSplat(tuple), Map(inv)) === IdentityTransducer()
12+
@test pop(Filter(isodd), MapSplat(tuple), Map(inv)) === Filter(isodd)
13+
@test pop(Map(isodd), Filter(isodd), MapSplat(tuple), Map(inv)) ===
14+
opcompose(Map(isodd), Filter(isodd))
15+
end
16+
17+
@testset "nitems" begin
18+
@test nitems(1:10) == 10
19+
@test nitems(error(x) for x in 1:10) == 10
20+
@test 1:10 |> Map(error) |> MapSplat(error) |> Scan(+) |> nitems == 10
21+
@test 1:10 |> Filter(isodd) |> Map(error) |> MapSplat(error) |> nitems == 5
22+
@test 1:10 |> Filter(isodd) |> Map(x -> x ÷ 3) |> Filter(isodd) |> nitems == 3
23+
end
24+
25+
end # module

test/utils.jl

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""
2+
==ₜ(x, y)
3+
4+
Check that _type_ and value of `x` and `y` are equal.
5+
"""
6+
==(_, _) = false
7+
==(x::T, y::T) where T = x == y

0 commit comments

Comments
 (0)