First n elements of array with Base.first

Is there a built-in way to get the first n elements of an array, even if n exceeds the length of the array?

This function demonstrates the desired behavior:

function first_n(a, n)
    n = min(n, length(a))
    a[1:n]
end

Just a[1:n] may result in a bounds error if n > length(a).

Could (or should) Base.first be used for this?

Seems like most Base.first({T}) methods could be extended to support Base.first({T}, ::Integer).

This is already supported for DataFrame and String:

first(df::AbstractDataFrame, n::Integer)
first(s::AbstractString, n::Integer)

Example of missing support for Array/Range (versus String).

julia> s = "foo"
"foo"

julia> a = 1:3
1:3

julia> first(s)
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> first(s, 5)
"foo"

julia> first(a)
1

julia> first(a, 5)
ERROR: MethodError: no method matching first(::UnitRange{Int64}, ::Int64)
1 Like

You can use end anywhere inside […], so perhaps you want a[1:min(n,end)]?

julia> a = 1:3

julia> f(n) = a[1:min(n,end)]

julia> f.(1:5)
5-element Array{UnitRange{Int64},1}:
 1:1
 1:2
 1:3
 1:3
 1:3
3 Likes

Thanks. That’s more compact than my custom function.

Now that makes me wonder why there’s a first(s::AbstractString, n::Integer) helper function for strings if the same can be accomplished with the technique you described.

julia> s = "foo"
"foo"

julia> s[1:min(5,end)]
"foo"

This mismatch may continue to be confusing for new users who expect first to work the same on Array as it it does on other Array-like types.

I didn’t know that first did that on strings. But note that they aren’t all that array-like, for instance first("abc′dêf",5) == "abc′dêf"[1:7] but [1:5] gives an error. Perhaps that’s why they got special commands?

Another way to write this, BTW, is f(n) = a[intersect(eachindex(a), 1:n)].

1 Like
f(arr,n)= arr[1:min(end,n)]

@rocco_sprmnt21, what would be the added value of your function compared to base: first(arr,n) ?

absolutely nothing.
In fact, I didn’t know at the time that the first function also had a second parameter - I learned this later in other discussions.

When I read (quickly, perhaps too much) the post I only saw that the chosen solution was:

f(n) = a[intersect(eachindex(a), 1:n)].

I came up with the one I sent and after quickly trying it out and also surprised by the fact that it just worked, I posted it straight away.
Without even seeing that the same idea had already been proposed in previous messages.