nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: JuliaCollections/DataStructures.jl
Pull request #584 (2020-03-03 19:47:43)

Make empty functions follow standard AbstractSet interface:
- Make `empty!()` return the modified set
- Implement `empty(s::SortedSet, t::Type)`
- Implement `Base.emptymutable(s::SortedSet, t::Type)` (alias for above)


This is a follow-up to #571, and implements some of the missing parts of the AbstractSets interface.


Patch:
diff --git a/Project.toml b/Project.toml
index 5e79352..d890fe5 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,6 +1,6 @@
 name = "DataStructures"
 uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
-version = "0.18.8"
+version = "0.19.0-DEV"
 
 
 [deps]
diff --git a/src/container_loops.jl b/src/container_loops.jl
index ad0cbdd..7177862 100644
--- a/src/container_loops.jl
+++ b/src/container_loops.jl
@@ -318,6 +318,6 @@ Base.eachindex(smd::SDMIncludeLast{SortedMultiDict{K,D,Ord}} where {K,D,Ord <: O
 Base.eachindex(ss::SSIncludeLast) = onlysemitokens(ss)
 
 
-Base.empty!(m::SAContainer) =  empty!(m.bt)
+Base.empty!(m::SAContainer) = (empty!(m.bt); m)
 Base.length(m::SAContainer) = length(m.bt.data) - length(m.bt.freedatainds) - 2
 Base.isempty(m::SAContainer) = length(m) == 0
diff --git a/src/sorted_set.jl b/src/sorted_set.jl
index 8547e91..e44fbd2 100644
--- a/src/sorted_set.jl
+++ b/src/sorted_set.jl
@@ -575,4 +575,9 @@ Returns a new `SortedDict`, `SortedMultiDict`, or `SortedSet` of the same
 type and with the same ordering as `sc` but with no entries (i.e.,
 empty). Time: O(1)
 """
-Base.empty(m::SortedSet{K,Ord}) where {K,Ord<:Ordering} = SortedSet{K,Ord}(orderobject(m))
+function Base.empty(m::SortedSet{K,Ord}, ::Type{U}=K) where {K,Ord<:Ordering,U}
+    return Base.emptymutable(m, U)
+end
+function Base.emptymutable(m::SortedSet{K,Ord}, ::Type{U}=K) where {K,Ord<:Ordering,U}
+    return SortedSet{U,Ord}(orderobject(m))
+end


Test patch:
diff --git a/test/test_sorted_containers.jl b/test/test_sorted_containers.jl
index 846d1f9..c1b2733 100644
--- a/test/test_sorted_containers.jl
+++ b/test/test_sorted_containers.jl
@@ -1537,6 +1537,15 @@ function testSortedSet()
     my_assert(typeof(m3empty) == SortedSet{String, ForwardOrdering})
     my_assert(eltype(m3empty) == String &&
        length(m3empty) == 0)
+    let m3empty_char = empty(m3, Char)
+        @test typeof(m3empty_char) == SortedSet{Char, ForwardOrdering}
+        Base.emptymutable(m3) == m3empty
+        Base.emptymutable(m3, Char) == m3empty_char
+    end
+    m3_reverse = SortedSet(DataStructures.FasterReverse(), ["orange", "yellow", "red"])
+    let m3empty_reverse_char = empty(m3_reverse, Char)
+        @test typeof(m3empty_reverse_char) == SortedSet{Char, DataStructures.FasterReverse}
+    end
     my_assert(isequal(m1,m2))
     my_assert(!isequal(m1,m3))
     my_assert(!isequal(m1, SortedSet(["blue"])))
Repository: JuliaCollections/DataStructures.jl
Pull request #602 (2020-04-02 21:55:07)

prefixsum for non-positive index in FenwickTree
I think that `prefixsum(ft, x)` should give `0` when `x<1` instead of an ArgumentError, just like how `sum([12,3,4,5,6][1:0]) = 0`.
In particular I was editing some code, and I assumed that `sum(my_array[1:x]) == prefixsum(FenwickTree(my_array), x)` would always be `true`. I was surprised that it wasn't `true` for all `x`, and think that this should be changed.


Patch:
diff --git a/src/fenwick.jl b/src/fenwick.jl
index 6de508c..871243b 100644
--- a/src/fenwick.jl
+++ b/src/fenwick.jl
@@ -83,6 +83,7 @@ julia> prefixsum(f, 3)
 """
 function prefixsum(ft::FenwickTree{T}, ind::Integer) where T
     sum = zero(T)
+    ind < 1 && return sum
     i = ind
     n = ft.n
     @boundscheck 1 <= i <= n || throw(ArgumentError("$i should be in between 1 and $n"))


Test patch:
diff --git a/test/test_fenwick.jl b/test/test_fenwick.jl
index 6e563b2..cf517e5 100644
--- a/test/test_fenwick.jl
+++ b/test/test_fenwick.jl
@@ -31,9 +31,11 @@
         incdec!(f1, 2, 6, 3)
         @test prefixsum(f1, 3) == 3
         @test prefixsum(f1, 7) == 5
+        
+        @test prefixsum(f1, 0) == 0
+        @test prefixsum(f1, -100) == 0
 
         @test_throws ArgumentError inc!(f1, 11)
-        @test_throws ArgumentError inc!(f1, 0)
     end
 
-end
\ No newline at end of file
+end
Repository: JuliaCollections/DataStructures.jl
Pull request #607 (2020-04-19 17:26:46)

Change `delete!()` for Sorted{Set,Dict} to no-op if key not found.
This makes SortedSet and SortedDict behave more like Julia Base's Set and Dict.

Fixes https://github.com/JuliaCollections/DataStructures.jl/issues/387


Patch:
diff --git a/src/sorted_dict.jl b/src/sorted_dict.jl
index 5182105..836c18b 100644
--- a/src/sorted_dict.jl
+++ b/src/sorted_dict.jl
@@ -453,8 +453,9 @@ Returns `sc`. Time: O(*c* log *n*)
 """
 @inline function delete!(m::SortedDict, k_)
     i, exactfound = findkey(m.bt, convert(keytype(m), k_))
-    !exactfound && throw(KeyError(k_))
-    delete!(m.bt, i)
+    if exactfound
+        delete!(m.bt, i)
+    end
     m
 end
 
diff --git a/src/sorted_set.jl b/src/sorted_set.jl
index f7661c0..fed24f7 100644
--- a/src/sorted_set.jl
+++ b/src/sorted_set.jl
@@ -229,8 +229,9 @@ Returns `sc`. Time: O(*c* log *n*)
 """
 @inline function delete!(m::SortedSet, k_)
     i, exactfound = findkey(m.bt,convert(keytype(m),k_))
-    !exactfound && throw(KeyError(k_))
-    delete!(m.bt, i)
+    if exactfound
+        delete!(m.bt, i)
+    end
     return m
 end
 


Test patch:
diff --git a/test/test_sorted_containers.jl b/test/test_sorted_containers.jl
index 0a5b2d0..67b08cd 100644
--- a/test/test_sorted_containers.jl
+++ b/test/test_sorted_containers.jl
@@ -1676,7 +1676,7 @@ end
     i2 = findkey(m,"bb")
     @test_throws BoundsError iterate(inclusive(m,i1,i2))
     @test_throws BoundsError iterate(exclusive(m,i1,i2))
-    @test_throws KeyError delete!(m,"a")
+    @test m === delete!(m,"a") # Okay to delete! nonexistent keys
     @test_throws KeyError pop!(m,"a")
     m3 = SortedDict((Dict{String, Int}()), Reverse)
     @test_throws ArgumentError isequal(m2, m3)
@@ -1697,7 +1697,7 @@ end
     @test_throws BoundsError last(m1)
 
     s = SortedSet([3,5])
-    @test_throws KeyError delete!(s,7)
+    @test s === delete!(s,7) # Okay to delete! nonexistent keys
     @test_throws KeyError pop!(s, 7)
     pop!(s)
     pop!(s)
Repository: JuliaCollections/DataStructures.jl
Pull request #637 (2020-06-25 15:22:32)

Add Base.copy, Base.copymutable for Sorted containers
Per our notes in https://github.com/JuliaCollections/DataStructures.jl/pull/571#issuecomment-594125382, the sorted containers currently don't implement `Base.copy(x)`, which is "Strongly Recommended," but not "Required."

However, they do all already provide `packcopy(m)`, and as far as i can tell, that seems like a perfectly reasonable implementation of copy: it returns a new container with all the same elements but not necessarily the same structure. ✔️ 

So this PR simply aliases `copy` and `copymutable` to `packcopy`. Does that sound good? :)
Thanks!
~Nathan



Patch:
diff --git a/Project.toml b/Project.toml
index e64c3da..6060895 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,6 +1,6 @@
 name = "DataStructures"
 uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
-version = "0.17.18"
+version = "0.17.19"
 
 [deps]
 InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
diff --git a/src/sorted_dict.jl b/src/sorted_dict.jl
index 836c18b..278cc13 100644
--- a/src/sorted_dict.jl
+++ b/src/sorted_dict.jl
@@ -535,6 +535,11 @@ function mergetwo!(m::SortedDict{K,D,Ord},
     end
 end
 
+# Standard copy functions use packcopy - that is, they retain elements but not
+# the identical structure.
+Base.copymutable(m::SortedDict) = packcopy(m)
+Base.copy(m::SortedDict) = packcopy(m)
+
 """
     packcopy(sc)
 
diff --git a/src/sorted_multi_dict.jl b/src/sorted_multi_dict.jl
index a55b8f7..02e5937 100644
--- a/src/sorted_multi_dict.jl
+++ b/src/sorted_multi_dict.jl
@@ -385,6 +385,11 @@ function mergetwo!(m::SortedMultiDict{K,D,Ord},
     end
 end
 
+# Standard copy functions use packcopy - that is, they retain elements but not
+# the identical structure.
+Base.copymutable(m::SortedMultiDict) = packcopy(m)
+Base.copy(m::SortedMultiDict) = packcopy(m)
+
 """
     packcopy(sc)
 
diff --git a/src/sorted_set.jl b/src/sorted_set.jl
index 7d9d31c..4bcf265 100644
--- a/src/sorted_set.jl
+++ b/src/sorted_set.jl
@@ -519,6 +519,11 @@ function issubset(iterable, m2::SortedSet)
     return true
 end
 
+# Standard copy functions use packcopy - that is, they retain elements but not
+# the identical structure.
+Base.copymutable(m::SortedSet) = packcopy(m)
+Base.copy(m::SortedSet) = packcopy(m)
+
 """
     packcopy(sc)
 


Test patch:
diff --git a/test/test_sorted_containers.jl b/test/test_sorted_containers.jl
index 67b08cd..846d1f9 100644
--- a/test/test_sorted_containers.jl
+++ b/test/test_sorted_containers.jl
@@ -1717,4 +1717,16 @@ end
     @test pop!(s,50, nothing) == nothing
     @test isempty(s)
 
+    # Test AbstractSet/AbstractDict interface
+    for m in [SortedSet([1,2]), SortedDict(1=>2, 2=>3), SortedMultiDict(1=>2, 1=>3)]
+        # copy()
+        let m1 = copy(m)
+            @test isequal(m1, m)
+            @test typeof(m1) === typeof(m)
+        end
+        let m1 = Base.copymutable(m)
+            @test isequal(m1, m)
+            @test typeof(m1) === typeof(m)
+        end
+    end
 end
Repository: JuliaCollections/DataStructures.jl
Pull request #784 (2022-01-03 17:37:44)

Implement resize! for CircularBuffer
Since #548 seems stale although being basically finished, except for a clean commit history, I'm just opening a new PR to get this merged.


Patch:
diff --git a/src/circular_buffer.jl b/src/circular_buffer.jl
index b5fcaad..ab56dfa 100644
--- a/src/circular_buffer.jl
+++ b/src/circular_buffer.jl
@@ -225,3 +225,25 @@ Base.@propagate_inbounds function Base.last(cb::CircularBuffer)
     @boundscheck (cb.length == 0) && throw(BoundsError(cb, 1))
     return cb.buffer[_buffer_index(cb, cb.length)]
 end
+
+"""
+    resize!(cb::CircularBuffer, n)
+
+Resize CircularBuffer to the maximum capacity of n elements.
+If n is smaller than the current buffer length, the first n elements will be retained.
+"""
+function Base.resize!(cb::CircularBuffer, n::Integer)
+    if n != capacity(cb)
+        buf_new = Vector{eltype(cb)}(undef, n)
+        len_new = min(length(cb), n)
+        for i in 1:len_new
+            @inbounds buf_new[i] = cb[i]
+        end
+
+        cb.capacity = n
+        cb.first = 1
+        cb.length = len_new
+        cb.buffer = buf_new
+    end
+    return cb
+end


Test patch:
diff --git a/test/test_circular_buffer.jl b/test/test_circular_buffer.jl
index ee99547..5c6af10 100644
--- a/test/test_circular_buffer.jl
+++ b/test/test_circular_buffer.jl
@@ -164,4 +164,28 @@
             @test Array(cb) == [21, 42, 42]
         end
     end
+
+    @testset "resize!" begin
+        @testset "resize an empty buffer" begin
+            cb = CircularBuffer{Int}(3)
+            resize!(cb, 10)
+            @test isempty(cb) && capacity(cb) == 10
+        end
+        @testset "resize a non empty buffer to smaller size" begin
+            cb = CircularBuffer{Int}(10)
+            append!(cb, -7:10)
+            resize!(cb, 6)
+            @test cb == 1:6
+            append!(cb, 7:8)
+            @test cb == 3:8
+        end
+        @testset "resize a non empty buffer to larger size" begin
+            cb = CircularBuffer{Int}(10)
+            append!(cb, -7:10)
+            resize!(cb, 15)
+            @test cb == 1:10
+            append!(cb, 11:20)
+            @test cb == 6:20
+        end
+    end
 end
Repository: JuliaCollections/DataStructures.jl
Pull request #856 (2023-05-29 16:56:37)

Allocs


Patch:
diff --git a/src/balanced_tree.jl b/src/balanced_tree.jl
index 62ef254..d54734f 100644
--- a/src/balanced_tree.jl
+++ b/src/balanced_tree.jl
@@ -242,8 +242,8 @@ function Base.empty!(t::BalancedTree23)
     initializeTree!(t.tree)
     t.depth = 1
     t.rootloc = 1
-    t.freetreeinds = Vector{Int}()
-    t.freedatainds = Vector{Int}()
+    empty!(t.freetreeinds)
+    empty!(t.freedatainds)
     empty!(t.useddatacells)
     push!(t.useddatacells, 1, 2)
     return nothing


Test patch:
diff --git a/test/test_sorted_containers.jl b/test/test_sorted_containers.jl
index 0878cfa..4e7e5e9 100644
--- a/test/test_sorted_containers.jl
+++ b/test/test_sorted_containers.jl
@@ -2027,6 +2027,12 @@ end
         
               
 
+function testSortedAllocs()
+    d = SortedDict{Int,Int}(Base.Order.ForwardOrdering())
+    x = @allocated empty!(d)
+    my_assert(x == 0)
+    true
+end
 
 @testset "SortedContainers" begin
     @test testSortedDictBasic()
@@ -2038,6 +2044,7 @@ end
     @test testSortedDictConstructors()
     @test testSortedMultiDictConstructors()
     @test testTokens()
+    @test testSortedAllocs()
 
 
     # test all the errors of sorted containers
@@ -2109,4 +2116,3 @@ end
         end
     end
 end
-
Repository: JuliaCollections/DataStructures.jl
Pull request #861 (2023-07-18 18:57:54)

do not drop repeat keys in Accumulate(::Pairs...)
Previously the `Accumulator(::Pairs...)` constructor first converted the sequence to a `Dict`, thus dropping repeated keys, which seemed counter what I would expect an accumulator to do (e.g. when used with a generator).


Patch:
diff --git a/src/accumulator.jl b/src/accumulator.jl
index 19065d5..feaed0a 100644
--- a/src/accumulator.jl
+++ b/src/accumulator.jl
@@ -14,7 +14,13 @@ end
 
 Accumulator{T, V}() where {T, V} = Accumulator{T, V}(Dict{T, V}())
 Accumulator(map::AbstractDict) = Accumulator(Dict(map))
-Accumulator(ps::Pair...) = Accumulator(Dict(ps))
+function Accumulator(p::Pair, ps::Pair...)
+    a = Accumulator(Dict(p))
+    for (k,v) in ps
+        inc!(a, k, v)
+    end
+    a
+end
 
 counter(T::Type) = Accumulator{T, Int}()
 counter(dct::AbstractDict{T, V}) where {T, V<:Integer} = Accumulator{T, V}(Dict(dct))


Test patch:
diff --git a/test/test_accumulator.jl b/test/test_accumulator.jl
index d6ba70d..173553f 100644
--- a/test/test_accumulator.jl
+++ b/test/test_accumulator.jl
@@ -51,7 +51,7 @@
         @test sum(ct) == 6
     end
 
-    @testset "From Pairs" begin 
+    @testset "From Pairs" begin
         acc = Accumulator("a" => 2, "b" => 3, "c" => 1)
         @test isa(acc,Accumulator{String,Int})
         @test haskey(acc,"a")
@@ -62,6 +62,15 @@
         @test acc["c"] == 1
     end
 
+    @testset "From Pairs with repeats" begin
+        acc = Accumulator("a" => 2, "b" => 3, "b" => 1)
+        @test isa(acc,Accumulator{String,Int})
+        @test haskey(acc,"a")
+        @test haskey(acc,"b")
+        @test acc["a"] == 2
+        @test acc["b"] == 4
+    end
+
     @testset "From Vector" begin
         ct2 = counter(["a", "a", "b", "b", "a", "c", "c"])
         @test isa(ct2, Accumulator{String,Int})
Repository: JuliaCollections/DataStructures.jl
Pull request #883 (2023-12-04 14:30:01)

fix append! iterable to MutableLinkedList
Currently `append!(MutableLinkedList{Int}(), [3])` would try to push the vector into the linked list instead of pushing elements into the linked list. 


Patch:
diff --git a/src/mutable_list.jl b/src/mutable_list.jl
index c38e7f3..cf085d5 100644
--- a/src/mutable_list.jl
+++ b/src/mutable_list.jl
@@ -165,7 +165,9 @@ end
 
 function Base.append!(l::MutableLinkedList, elts...)
     for elt in elts
-        push!(l, elt)
+        for v in elt
+            push!(l, v)
+        end
     end
     return l
 end
@@ -213,6 +215,14 @@ function Base.push!(l::MutableLinkedList{T}, data) where T
     return l
 end
 
+function Base.push!(l::MutableLinkedList{T}, data1, data...) where T
+    push!(l, data1)
+    for v in data
+        push!(l, v)
+    end
+    return l
+end
+
 function Base.pushfirst!(l::MutableLinkedList{T}, data) where T
     oldfirst = l.node.next
     node = ListNode{T}(data)


Test patch:
diff --git a/test/test_mutable_list.jl b/test/test_mutable_list.jl
index c900b7b..d3bf749 100644
--- a/test/test_mutable_list.jl
+++ b/test/test_mutable_list.jl
@@ -101,9 +101,13 @@
                     @test l2 == MutableLinkedList{Int}()
                     @test collect(l) == collect(MutableLinkedList{Int}(1:2n...))
                     l3 = MutableLinkedList{Int}(1:n...)
-                    append!(l3, n+1:2n...)
+                    append!(l3, n+1:2n)
                     @test l3 == MutableLinkedList{Int}(1:2n...)
                     @test collect(l3) == collect(MutableLinkedList{Int}(1:2n...))
+                    l4 = MutableLinkedList{Int}(1:n...)
+                    push!(l4, n+1:2n...)
+                    @test l4 == MutableLinkedList{Int}(1:2n...)
+                    @test collect(l4) == collect(MutableLinkedList{Int}(1:2n...))
                 end
 
                 @testset "delete" begin
Repository: JuliaLinearAlgebra/GenericLinearAlgebra.jl
Pull request #73 (2021-03-26 16:31:30)

Enable full keyword for svd.
This is the only piece of functionality from GenericSVD that is currently missing.


Patch:
diff --git a/src/svd.jl b/src/svd.jl
index 68944df..4d09e6a 100644
--- a/src/svd.jl
+++ b/src/svd.jl
@@ -552,11 +552,15 @@ function LinearAlgebra.svd!(A::StridedMatrix{T};
 
     # Form the matrices U and Vᴴ by combining the singular vector matrices of the bidiagonal SVD with the Householder reflectors from the bidiagonal factorization.
     if istriu(_B)
-        U  = BF.leftQ*F.U
+        U  = Matrix{T}(I, m, full ? m : n)
+        U[1:n,1:n] = F.U
+        lmul!(BF.leftQ, U)
         Vᴴ = F.Vt*BF.rightQ'
     else
         U  = BF.leftQ*F.V
-        Vᴴ = (BF.rightQ*F.U)'
+        Vᴴ = Matrix{T}(I, full ? n : m, n)
+        Vᴴ[1:m,1:m] = F.U'
+        rmul!(Vᴴ, BF.rightQ')
     end
 
     s = F.S


Test patch:
diff --git a/test/svd.jl b/test/svd.jl
index 1ff00c3..2dec80e 100644
--- a/test/svd.jl
+++ b/test/svd.jl
@@ -44,6 +44,11 @@ using Test, GenericLinearAlgebra, LinearAlgebra, Quaternions
         Fbig = svd(Abig)
         @test abs.(F.U'Float64.(Fbig.U)) ≈ I
         @test abs.(F.V'Float64.(Fbig.V)) ≈ I
+
+        F    = svd(A, full=true)
+        Fbig = svd(Abig, full=true)
+        @test abs.(F.U'Float64.(Fbig.U)) ≈ I
+        @test abs.(F.V'Float64.(Fbig.V)) ≈ I
     end
 
     @testset "Issue 54" begin
@@ -53,4 +58,5 @@ using Test, GenericLinearAlgebra, LinearAlgebra, Quaternions
         U, S, V = svd(A)
         @test A ≈ U*Diagonal(S)*V'
     end
+
 end
Repository: JuliaML/TableTransforms.jl
Pull request #22 (2022-01-26 00:35:55)

Type Stable Functional
A small change to make the Functional type stable


Patch:
diff --git a/src/transforms/functional.jl b/src/transforms/functional.jl
index 4d0458c..aa772db 100644
--- a/src/transforms/functional.jl
+++ b/src/transforms/functional.jl
@@ -7,8 +7,8 @@
 
 The transform that applies a `function` elementwise.
 """
-struct Functional <: Colwise
-  func::Function
+struct Functional{F} <: Colwise
+  func::F
 end
 
 isrevertible(transform::Functional) =
@@ -23,7 +23,7 @@ inverse(::typeof(sin))  = asin
 inverse(::typeof(asin)) = sin
 
 # fallback to nothing
-inverse(::Function) = nothing
+inverse(::Any) = nothing
 
 colcache(::Functional, x) = nothing
 


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index fa7b563..e5791c2 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -20,6 +20,13 @@ islinux = Sys.islinux()
 visualtests = !isCI || (isCI && islinux)
 datadir = joinpath(@__DIR__,"data")
 
+# for functor tests in Functional testset
+struct Polynomial{T<:Real}
+  coeffs::Vector{T}
+end
+Polynomial(args::T...) where {T<:Real} = Polynomial(collect(args))
+(p::Polynomial)(x) = sum(a * x^(i-1) for (i, a) in enumerate(p.coeffs))
+
 # list of tests
 testfiles = [
   "distributions.jl",
diff --git a/test/transforms.jl b/test/transforms.jl
index 1fcbef1..f7770ed 100644
--- a/test/transforms.jl
+++ b/test/transforms.jl
@@ -358,6 +358,19 @@
     n, c = apply(T, t)
     @test t == n
     @test isrevertible(T) == false
+
+    # functor tests
+    x = rand(1500)
+    y = rand(1500)
+    t = Table(; x, y)
+    f = Polynomial(1, 2, 3) # f(x) = 1 + 2x + 3x²
+    T = Functional(f)
+    n, c = apply(T, t)
+    @test f.(x) == n.x
+    @test f.(y) == n.y
+    @test all(≥(1), n.x)
+    @test all(≥(1), n.y)
+    @test isrevertible(T) == false
   end
 
   @testset "EigenAnalysis" begin
Repository: JuliaML/TableTransforms.jl
Pull request #33 (2022-04-03 20:07:43)

Implementing ColSpec interface in DropMissing
This PR implements the ColSpec interface in DropMissing. This is the last part of the ColSpec interface implementation in the current code.


Patch:
diff --git a/src/transforms/filter.jl b/src/transforms/filter.jl
index e44849c..0004735 100644
--- a/src/transforms/filter.jl
+++ b/src/transforms/filter.jl
@@ -42,6 +42,7 @@ const VecOrTuple{T} = Union{Vector{T}, NTuple{N, T}} where {T, N}
 
 """
     DropMissing()
+    DropMissing(:)
 
 Drop all rows with missing values in table.
 
@@ -55,20 +56,35 @@ Drop all rows with missing values in selects columns `col₁`, `col₂`, ..., `c
 
 Drop all rows with missing values in columns that match with `regex`.
 """
-DropMissing() = Filter(row -> all(!ismissing, row))
+struct DropMissing{S<:ColSpec} <: Stateless
+  colspec::S
+end
 
-DropMissing(cols::VecOrTuple{T}) where {T<:Union{Symbol, Integer}} =
-  Filter(row -> all(!ismissing, getindex.(Ref(row), cols)))
+DropMissing(::Tuple{}) = throw(ArgumentError("Cannot create a DropMissing object with empty tuple."))
 
-DropMissing(cols::VecOrTuple{T}) where {T<:AbstractString} =
-  DropMissing(Symbol.(cols))
+DropMissing() = DropMissing(:)
 
 DropMissing(cols::T...) where {T<:ColSelector} =
   DropMissing(cols)
 
-function DropMissing(regex::Regex)
-  Filter() do row
-    cols = _filter(regex, propertynames(row))
-    all(!ismissing, getindex.(Ref(row), cols))
-  end
+isrevertible(::Type{<:DropMissing}) = true
+
+_ftrans(::DropMissing{Colon}, table) =
+  Filter(row -> all(!ismissing, row))
+
+function _ftrans(transform::DropMissing, table)
+  allcols = Tables.columnnames(table)
+  cols = _filter(transform.colspec, allcols)
+  Filter(row -> all(!ismissing, getindex.(Ref(row), cols)))
+end
+
+function apply(transform::DropMissing, table)
+  ftrans = _ftrans(transform, table)
+  newtable, fcache = apply(ftrans, table)
+  newtable, (ftrans, fcache)
+end
+
+function revert(::DropMissing, newtable, cache)
+  ftrans, fcache = cache
+  revert(ftrans, newtable, fcache)
 end


Test patch:
diff --git a/test/transforms.jl b/test/transforms.jl
index 7450a27..7e9520a 100644
--- a/test/transforms.jl
+++ b/test/transforms.jl
@@ -603,6 +603,22 @@
     n1, c1 = apply(T, t)
     n2 = reapply(T, t, c1)
     @test n1 == n2
+
+    # throws: empty tuple
+    @test_throws ArgumentError DropMissing(())
+
+    # throws: empty selection
+    @test_throws AssertionError apply(DropMissing(r"g"), t)
+    @test_throws AssertionError apply(DropMissing(Symbol[]), t)
+    @test_throws AssertionError apply(DropMissing(String[]), t)
+
+    # throws: columns that do not exist in the original table
+    @test_throws AssertionError apply(DropMissing(:g, :h), t)
+    @test_throws AssertionError apply(DropMissing([:g, :h]), t)
+    @test_throws AssertionError apply(DropMissing((:g, :h)), t)
+    @test_throws AssertionError apply(DropMissing("g", "h"), t)
+    @test_throws AssertionError apply(DropMissing(["g", "h"]), t)
+    @test_throws AssertionError apply(DropMissing(("g", "h")), t)
   end
 
   @testset "Rename" begin