String#presence
is a very useful method for when dealing with String?
typed variables to ensure there is a non-blank, non-nil value. However, I sometimes notice myself wanting to something similar, but with an array. That is, perform some logic on an array that is non-nil, and not empty. Of which, having this method on Array
, or even Enumerable
could be quite handy.
I’m starting this off as a thread versus an issue as I’m not 100% convinced just yet. Like is this actually as commonly useful as I think it is? And/or is there actually a meaningful different between Array(String)? = nil
and Array(String) = [] of String
from a performance perspective?
The main benefit is when those arrays are mostly empty. Using nil avoids the GC allocation.
In the compiler is common, but I am not sure it’s really needed in most application’s code.
String#presence
can be handy for handling HTML forms where ""
functionally equivalent to nil
. Most other usage I see of it would be better served by an actual nil
.
One of the most confusing things for me when reading code is multiple-negative naming, like case_insensitive: false
, so sometimes it can be nice to have a method where a method with a truthy return value indicates that it is a useful value. String#presence
does this, but for arrays that’s already served by Array#any?
.
Based on my experience with Rails, I feel like it would be used a lot, but I would draw a distinction between it being used and it being useful. I see if foo.present?
in a lot of Rails codebases where if foo
or if foo.any?
would convey more information. Extrapolating from that (with the usual caveats about extrapolation), I think this would be misused more than it would be used correctly.
Usually when I need to accommodate an array not being provided (for example, some JSON APIs will simply not return empty array properties), I use something like this:
require "json"
struct Foo
include JSON::Serializable
getter my_array : Array(String) { [] of String }
getter? my_array
end
foo = Foo.from_json(NamedTuple.new.to_json)
if array = foo.my_array?
# ...
end
pp foo
# Foo(@my_array=nil) -- no allocation
foo.my_array.each do |item|
# ...
end
pp foo
# Foo(@my_array=[]) -- allocated the array
This way, regardless of whether the remote API returns the array, I have the option to YOLO my way into it, which may allocate unnecessarily, or I can use the ?
version of the method if I want to avoid unnecessary allocations when the array is not provided.
1 Like
Previous related discussions: