diff --git a/Project.toml b/Project.toml index 13084835..8e58b6a3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TimeDag" uuid = "91963e24-6e66-4132-aeb8-436d9f37dbc7" authors = ["Invenia Technical Computing Corporation", "Tom Gillam "] -version = "0.1.23" +version = "0.1.24" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/docs/src/reference/misc_ops.md b/docs/src/reference/misc_ops.md index 529a7cce..31e07aef 100644 --- a/docs/src/reference/misc_ops.md +++ b/docs/src/reference/misc_ops.md @@ -18,6 +18,7 @@ Base.rand ```@docs Base.filter Base.skipmissing +zap_until ``` ## Type conversion diff --git a/src/TimeDag.jl b/src/TimeDag.jl index a5b92cd8..182ff4d1 100644 --- a/src/TimeDag.jl +++ b/src/TimeDag.jl @@ -27,6 +27,8 @@ export block_node, constant, empty_node, iterdates, pulse, tea_file # Alignment nodes. export active_count, align, align_once, coalign, count_knots, first_knot, lag, right, left export prepend, throttle +# Conditional nodes. +export zap_until # Type conversion export convert_value # Other nodes diff --git a/src/ops/conditional.jl b/src/ops/conditional.jl index ffece8c4..5a6edf42 100644 --- a/src/ops/conditional.jl +++ b/src/ops/conditional.jl @@ -48,3 +48,21 @@ function Base.skipmissing(x::Node) _is_empty(x) && return empty_node(T) return obtain_node((x,), SkipMissing{T}()) end + +# TODO a more efficient implementation for this would simply slice blocks as required, +# rather than evaluate the condition knotwise. +struct ZapUntil{T} <: UnaryNodeOp{T} + time::DateTime +end +stateless_operator(::ZapUntil) = true +operator!(op::ZapUntil{T}, t::DateTime, x) where {T} = t < op.time ? Maybe{T}() : Maybe(x) + +""" + zap_until(x::Node, t) -> Node + +Given a node `x`, return a node in which all knots strictly before `t` are omitted. +""" +function zap_until(x::Node{T}, t::DateTime) where {T} + _is_empty(x) && return x + return obtain_node((x,), ZapUntil{T}(t)) +end diff --git a/test/ops/conditional.jl b/test/ops/conditional.jl index 24eb4348..2a345680 100644 --- a/test/ops/conditional.jl +++ b/test/ops/conditional.jl @@ -56,4 +56,26 @@ end ]) end +@testset "zap_until" begin + n = empty_node(Int64) + @test zap_until(n, DateTime(2000)) === n + + @test _eval(zap_until(n1, DateTime(2000, 1, 3))) == + Block([ + DateTime(2000, 1, 3) => 3, + DateTime(2000, 1, 4) => 4 + ]) + + @test _eval(zap_until(n_boolean, DateTime(2000, 1, 3))) == + Block([ + DateTime(2000, 1, 3) => true, + DateTime(2000, 1, 4) => true + ]) + + for cutoff in first(b4.times):Minute(133):last(b4.times) + @test _eval(zap_until(n4, cutoff)) == + TimeDag._slice(b4, cutoff, last(b4.times) + Hour(1)) + end +end + #! format: on