How do I detect the last iteration in a loop, e.g. generation INSERT INTO statement for several records:
sql = "INSERT INTO table VALUES "
for oneRecord in records # guaranteed that records is not empty
sql *= "($val1, $val2, $oneRecord)"
if islast # this statement I am asking about
sql *= ";"
else
sql *= ", "
end
end
I know I could use lenght and count iteration, but I think there must be something nicer.
records = Iterators.Stateful(1:10)
for i in records
if peek(records) == nothing
println(" <- that was the penultimate value")
end
println(i)
end
1
2
3
4
5
6
7
8
9
<- that was the penultimate value
10
The idiom I like to use to solve this particular problem is to use join. join can be used to join together an Array of strings with a chosen separator. E.g:
sql = "INSERT INTO table VALUES "
inserts = ["($val1, $val2, $oneRecord)" for oneRecord in records]
sql *= join(inserts, ", ")
I’ve used an array comprehension to create a temporary Array inserts that have the nicely formatted strings you want to join together
(This does not answer your actual question of how to detect the last iteration: it’s a different way of solving the question in your code - which is a pattern I came across in my own work all the time.)
Suggested code works with UInt8, but when I use strings, as it is in my case:
records=["a", "b", "c"]
I am getting:
ERROR: MethodError: no method matching peek(::Array{String,1}, ::Type{UInt8})
Closest candidates are:
peek(::Base.Iterators.Stateful, ::Any) at iterators.jl:1274
peek(::REPL.Terminals.TTYTerminal, ::Type{T}) where T at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\REPL\src\Terminals.jl:163
peek(::Base.AbstractPipe, ::Type{T}) where T at io.jl:358
There is no way, in general, to know that an iteration is done until you actually get nothing back from iterate So you need to remember two values while iterating. This is how join does it:
sql = "INSERT INTO table VALUES "
for (i, oneRecord) in enumerate(records) # guaranteed that records is not empty
i != 1 && sql *= ", "
sql *= "($val1, $val2, $oneRecord)"
end
sql *= ";"
I would also use join, but if you insist on a loop, you can also check for the first element, and reorganize the logic a bit:
using InterTools
sql = "INSERT INTO table VALUES "
for (isfirst, oneRecord) in IterTools.flagfirst(records)
if !isfirst
sql *= ", "
end
sql *= "($val1, $val2, $oneRecord)"
end
sql *= ";"
The other solutions look good to me too, but to make the Iterators.Stateful one work, you just need to wrap the iterator (whatever it is, vector of strings or anything else) in Iterators.Stateful, and then just use the wrapper, e.g.
records=["a", "b", "c"]
itr = Iterators.Stateful(records)
for i in itr
if peek(itr) == nothing
println(" <- that was the penultimate value")
end
println(i)
end
yields
a
b
<- that was the penultimate value
c
This is just a convienent way of doing the “remember values while iterating” for you.