function foo(::Val{N}, nzindices::Vector, nzvalues::Vector) where N
function f(i)
idx = findfirst(isequal(i),nzindices)
if isnothing(idx)
return zero(eltype(nzvalues))
else
return nzvalues[idx]
end
end
return ntuple(f,Val(N))
end

function foo2(::Val{N}, nzindices, nzvalues) where N
res = ntuple(i -> zero(eltype(nzvalues)),Val{N}())
for i in 1:length(nzindices)
res = Base.setindex(res,nzvalues[i],nzindices[i])
end
return res
end

I suppose that the first method creates an NTuple once, but calls a function with conditionals. at low N, the cost of the function might be dominated by that call.
The second method creates nz NTuples, that may be fast at low N, but i suppose there is a limit on that. if you want the best of both worlds, you can combine both, if you find the N threshold that most suits your enviroment