Extract single element from container

question

#1

Every now and then I have a container like [1] or Set(["foo"]), which contains only a single element and I want to extract it. Is there a function that does this somewhere? It should

  1. Throw an error, if the container is empty or has more then one element.
  2. only assume the iteration protocol, e.g. not use length.

If there is nothing official, the following does the trick, what would be a good place to add a PR for it?

function single(iter)
    s1 = start(iter)
    if done(iter, s1)
        throw(ArgumentError("iter $iter is empty."))
    end
    item, s2 = next(iter, s1)
    
    if !done(iter, s2) 
        throw(ArgumentError("iter $iter has more then one element."))
    end
    item
end

#2
julia> first(Set([1]))
1

julia> first([1])
1

julia> first([])
ERROR: BoundsError: attempt to access 0-element Array{Any,1} at index [1]
Stacktrace:
 [1] first(::Array{Any,1}) at ./abstractarray.jl:135

julia> first(Set())
ERROR: ArgumentError: collection must be non-empty
Stacktrace:
 [1] first(::Set{Any}) at ./abstractarray.jl:153

does the extraction and checking for emptyness, but will not throw an error if you have multiple elements.


#3

I can’t find it now but I know there’s been an issue with this exact feature request.


#4

Yeah I often use

single(iter) = (@assert length(iter) ==1; first(iter))

One common case, where this often fails due to lack of length is:

single(filter(ishighlander, iter))

#5

Do you remember something that makes the search easier? Repos? proposed name?..


#6

Found it https://github.com/JuliaLang/julia/pull/25078. It was a PR.


#7

This looks like something that could live in a package in the meantime.


#8

Yes, you can find the only function in SplitApplyCombine.jl but I haven’t got around to publishing that to METADATA yet.