Maxim Veksler
Maxim Veksler

Reputation: 30232

Using first element of array or as is if object is not an array

Let's assume method a can return either an array or a single element

def a(n)
  if n == 0
    return '0'
  else
    return '1', '2'
  end
end

irb(main):008:0> a(0)
=> "0"
irb(main):009:0> a(1)
=> ["1", "2"]

What would be the best "ruby" way to express the following:

irb(main):013:0> a(0).<???>.to_i
=> 0
irb(main):014:0> a(1).<???>.to_i
=> 1

I'm looking for an elegant syntax to achieve the above. I obviously can implement a function what would do dynamic type inspection and would either return the first element of just the input variable as is..

Upvotes: 0

Views: 104

Answers (4)

Stefan
Stefan

Reputation: 114238

You could use array decomposition to assign only the first value to a variable:

x, = a(0) #=> "0"
x         #=> "0"

x, = a(1) #=> ["1", "2"]
x         #=> "1"

x, = ... is equivalent to x, * = ... or (x, *) = ...

Or as a helper method with array decomposition (other link, extra parentheses are required here):

def first((x, *))
  x
end

first a(0) #=> "0"
first a(1) #=> "1"

A different approach is to yield the values:

def a(n)
  if n == 0
    yield '0'
  else
    yield '1', '2'
  end
end

a(0) { |x| x } #=> "0"
a(1) { |x| x } #=> "1"

a(0) { |*args| args } #=> ["0"]
a(1) { |*args| args } #=> ["1", "2"]

This also works nicely with Enumerator:

def a(n)
  return enum_for(:a, n) unless block_given?
  if n == 0
    yield '0'
  else
    yield '1', '2'
  end
end

a(0).next        #=> "0"
a(1).next        #=> ["1", "2"]

a(0).next_values #=> ["0"]
a(1).next_values #=> ["1", "2"]

Upvotes: 2

Cary Swoveland
Cary Swoveland

Reputation: 110735

def a(n)
  n.zero? ? '0' : '10', '20'
end

[*a(0)].first #=> "0"
[*a(7)].first #=> "10"

Upvotes: 1

engineersmnky
engineersmnky

Reputation: 29588

While I agree with all the comments that this is not a great idea.

Technically a(n)[0].to_i works in both cases

a(0)[0].to_i
#=> 0
a(1)[0].to_i
#=> 1 

String#[] returns the nth character in the same fashion that Array#[] returns the nth element.

Based on your example even if the first return was a Fixnum rather than a String. Fixnum#[] would still work :) but only because 0[0] returns the bit reference which just so happens to be 0

This is what duck typing is all about but it has its caveats such as if a(0) was to return 2 instead of '0' then you would get 0 e.g. 2[0].to_i #=> 0 because the binary representation is 10 and you are asking for the least significant bit. (first position from the right).

Since you have full control of what a actually is it makes more sense to maintain consistency in the return value from a. It also makes documenting the method far simpler.

def a(n) 
  n == 0 ? ['0'] : ['1','2']
end 

Upvotes: 1

Mickey Sheu
Mickey Sheu

Reputation: 768

You could cast it into an array using Array():

Array(a(n)).first

> Array(["1","2"]).first
 => "1" 
> Array("0").first
 => "0" 

A better solution (if you controlled a(n)) would be to return a consistent type from your method.

Upvotes: 4

Related Questions