I'm doing API calls that will conditionally return a couple different elements. My code is currently:
if array['productId']
value = array['productId'][0]
end
I feel like there is a more succinct way of doing this. Some Ruby magic.
A better way :
value = array['productId'][0] if array['productId']
However, array['productId'][0] is not ruby natural. What does your array consist of ?
Since Ruby 2.3.0, the shortest way is Array#dig:
array.dig('productId', 0)
http://ruby-doc.org/core-2.3.0_preview1/Array.html#method-i-dig
I am not sure if you are using value just temporarily or actually using it later, and what you want to do with value when the condition is not met. If you want to return nil for missing keys, then
array['productId'].to_a[0]
can work. Otherwise, SpyrosP's answer will be the best.
This might be a bit pedantic, but to make sure it works in all circumstances, you should not check 'not-nil', but rather that it is indexable; something like this:
value = array['productId'][0] if array['productId'].is_a? Array
Or even better:
value = array['productId'][0] if array['productId'].respond_to? '[]'
Otherwise your code will fail if array['productId'] == 2 (which on the other hand seems reasonable, given the key used - I would have gone product_ids instead).
You could use a ternary:
value = array['productId'].nil? ? nil : array['productId'][0]
Your code pattern looks OK; it's possible to be slightly shorter...
value = (t = x['productId']) && t[0]
Using the maybe pattern of Ick, terse and explicit:
value = array['productId'].maybe[0]
While I think your code is fine (although I'd prefer SpyrosP's one-line version), you have some possibilities:
Rails has Object#try, which would let you do either array['productId'].try(:[], 0) or array['productId'].try(:at, 0).
Some people like the andand gem, which defines Object#andand and is used like array['productId'].andand[0].
Ha, I love all the options here. But since I didn't see what I use most, I'll add one more!
value = array['productId'] && array['productId'].first
(I prefer .first to [0] because it reads a little better)
This presumes you'll have an array in array['productId'], which is the right way to do it (rather than type-checking).
Otherwise, the biggest diference between this and your original code, is that this results in value having nil assigned to it if the array doesn't have anything, whereas your original results in value not being defined (which may cause errors, depending on how you use it down the road).
Hope that helps!
Related
I have the following code, and I feel like there is probably a cleaner way to access the objects that I want:
id = job.args.size > 0 && job.args[0]['arguments'].size > 0 ? job.args[0]['arguments'][0] : nil
This is what dig is for:
id = job.args.dig(0, 'arguments', 0)
dig is defined for Array, Hash, and Struct so it can deal with most kinds of nested structures.
It's hard to give advice on how to better organise the code if we can only see one line of it! Dealing with a messy object like this indicates you may have a wider design issue that could improve the code quality. However...
Based on the above, a "happy scenario" is if:
job.args == [{"arguments"=>["foo"]}]
i.e. An array whose first element is a hash with key 'arguments', which maps to a non-empty array. This looks very messy!
However, you can simplify this to:
job.args.dig(0, 'arguments', 0)
This is applying Array#dig (note: there's also Hash#dig) to chain the method calls and gracefully respond with nil if any fail.
This answer assumes you are using ruby version >= 2.3.0, since this is when dig was added to the language. If you are running an older version, you could also use this gem to back-port the feature.
Sometimes when I'm passing a method name as an argument, I find myself wishing for an identity function such that any_obj.send(:identity) == any_obj, so instead of this:
(transform.nil?) ? my_obj : my_obj.send(transform)
I could just write
my_obj.send(transform || :identity)
(This is a bit of a contrived example -- an identity function could do more than save a few keystrokes in more complicated examples.)
It would be easy enough to open up the definition of Object and add it, but is there something already there that I've overlooked? Does anyone else want this as well?
P.S.: I know my example should really be stated any_obj.send(:identity).equal?(any_obj), but sometimes pedantry obscures the question.
Yes. You're looking for Object#itself, which is available in Ruby 2.2+.
From the docs:
itself → an_object
Returns obj.
string = 'my string' #=> "my string"
string.itself.object_id == string.object_id #=> true
Your question is related to this one. It seems that now, Ruby is going to have #itself method.
There is not any for now, but Matz has expressed an opinion favoring such method to be implemented: https://bugs.ruby-lang.org/issues/6373. It is likely to be implemented in future versions of Ruby.
I have to use String.scan function, which returns empty array if there is no match.
I wanted to assign a variable with the scan function and check it there is a match, but unfortunately I cannot do that because it won't return nil or false on no match.
I wanted to do this (1 line):
if ip = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
...
#use ip
end
but because it won't return nil on no match I must do:
ip_match = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
unless ip_match.empty?
#use ip
end
Is there some more elegant way to write this - to be able to do assignment and empty check at the same time or some other way to beautify the code?
Thanks
Since scan returns an array, and even if you are sure there would be only one result, you could do this.
str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/).each do |ip|
#use ip
end
There's a difference between elegant and cryptic or "concise".
In Perl you'll often see people write something equivalent to:
if (!(ip = str.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)).empty?)
It's a bit more concise, terse, tight, whatever you want to call it. It also leads to maintenance issues because of the = (equate) vs. what should normally be an equality test. If the code is passed to someone who doesn't understand the logic, they might mistakenly "correct" that, and then break the code.
In Ruby it's idiomatic to not use equate in a conditional test, because of the maintenance issue, and instead use the assignment followed by a test. It's clearer code.
Personally, I prefer to not use unless in that sort of situation. It's an ongoing discussion whether unless helps generate more understandable code; I prefer if (!ip_match.empty?) because it reads more like we'd normally talk -- I seldom start a statement with unless in conversation. Your mileage might vary.
I would preferably do something like this using String helper match
ip_validator = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
# match return nil if no match
if str.match ip_validator
# blah blah blah.....
end
help me keep code dry and clean.
May be this is not the most elegant , looking for others if any :)
Your ip_validator regex seems to be week check this out Rails 3: Validate IP String
Which way of writing this condition is better?
1)
(self.expense_gl_dist_code.dist_type == GlDistCode::PERCENTAGE || self.expense_gl_dist_code.dist_type == GlDistCode::MIXED)
2)
["GlDistCode::PERCENTAGE","GlDistCode::MIXED"].include?(self.expense_gl_dist_code.dist_type)
I find the second clearer for two reasons:
1) In the second version the elements being checked for are all next to each other, separated by commas. In the first version there's always self.expense_gl_dist_code.dist_type ==, so it's less easy to scan them all at once.
2) In the second version it's immediately obvious that all elements are checked for the same condition, while in the first version it could say something like
dist_type == GlDistCode::PERCENTAGE || dist_type == GlDistCode::MIXED || dist_type != GlDistCode::WHATEVER
and you might not notice right away.
The first way is much clearer and therefore to be preferred to the slightly obfuscated second option.
If you are just comparing two elements, I'd say either is fine.
I'm more inclined to the second version, because you can then include all the elements you want to validate against into a single variable, and name it. For example
ALLOWED_TYPES = [GldDistCode::PERCENTAGE, GlDistCode::MIXED]
then
if ALLOWED_TYPES.include?(dist_type)
is more legible IMHO.
BTW, you're using strings ("GldDistCode::PERCENTAGE") instead of the actual value which you intended.
A Ruby dev I know asked this; my answer is below... Are there other, better reasons?
Why do so many Ruby programmers do
"#{string}"
rather than
string
since the second form is simpler and more efficient?
Is this a common idiom for Ruby developers? I don't see it that much.
Smaller changes when you later need to do more than simply get the value of the string, but also prepend/append to it at the point of use seems to be the best motivation I can find for that idiom.
There is only one case where this is a recommended idiom :
fname = 'john'
lname = 'doe'
name = "#{fname} #{lname}"
The code above is more efficient than :
name = fname + ' ' + lname
or
name = [fname, lname].join(' ')
What's the broader context of some of the usages? The only thing I can come up with beyond what's already been mentioned is as a loose attempt at type safety; that is, you may receive anything as an argument, and this could ensure that whatever you pass in walks like a duck..or, well, a string (though string.to_s would arguably be clearer).
In general though, this is probably a code smell that someone along the way thought was Best Practices.
I use this kind of code, so that I can pass nil as string and it still will work on a string, rather than seeing some exceptions flying:
def short(string = nil)
"#{string}"[0..7]
end
And it's easier/faster to append some debug code, if it's already in quotes.
So in short: It's more convenient.
Interesting answers, everyone. I'm the developer who asked the original question. To give some more context, I see this occasionally at my current job, and also sometimes in sample code on the Rails list, with variables that are known in advance to contain strings. I could sort of understand it as a substitute for to_s, but I don't think that's what's going on here; I think people just forget that you don't need the interpolation syntax if you're just passing a string variable.
If anyone tried to tell me this was a best practice, I'd run away at top speed.
maybe it is easy way to convert any to string? Because it is the same as call to_s method. But it is quite strange way :).
a = [1,2,3]
"#{a}"
#=> "123"
a.to_s
#=> "123"
I could image this being useful in cases where the object being interpolated is not always a String, as the interpolation implicitly calls #to_s:
"#{'bla'}" => "bla"
"#{%r([a-z])}" => "(?-mix:[a-z])"
"#{{:bla => :blub}}" => "blablub"
May make sense when logging something, where you don't care so much about the output format, but never want an error because of a wrong argument type.