Ruby: Object.to_a replacement - ruby

I need to convert a passed in argument (single object or collection) to an Array. I don't know what the argument is. If it is an Array already, I want to leave it, otherwise create a one-element array from it. I'm looking to allow both method(:objs => obj) and method(:objs => [obj1, obj2])
This seems to be the best way (Array#to_a returns self):
arg = arg.to_a
But the ruby docs say Object#to_a will soon be obsolete. Is there convenient replacement?
Anything more succinct than this?
arg = arg.respond_to?(:to_a) ? arg.to_a : [arg]

Use the method Kernel#Array:
Array([1,2,3]) #=> [1, 2, 3]
Array(123) #=> [123]
Yes it may look like a class at first but this is actually a method that starts with a capital letter.

Wow, someone just necromanced a really old thread. :-O But since I don't see it included yet, I'll add one more way for completeness' sake:
arg = [*arg]
This will splat the argument if it already is an array (thus removing one level of nesting) or create a one-argument array otherwise:
arg = [1,2,3]
[*arg] #=> [1, 2, 3]
arg = 1
[*arg] #=> [1]

It seems only Object.to_a is deprecated, removing a default to_a and forcing each class to define its own (e.g., Hash.to_a).
self.to_a #=> -:1: warning: default `to_a' will be obsolete
"hello".to_a #=> ["hello"]
Time.new.to_a #=> [39, 54, 8, 9, 4, 2003, 3, 99, true, "CDT"]
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["a", 100], ["c", 300], ["d", 400]]
If your argument is an instance of Object, try:
Hash.new(obj).to_a
#Daniel [comment to Ollivier]: The point is that I don't know what the argument is. If it is an array already, I want to leave it, otherwise create a one-element array.
If that's the case, try:
obj = [obj] if !obj.is_a?(Array)

You can take the duck typing aproach if that suits the problem better, make a list of all the methods you need, and check if the object already have them, if not, make it an array:
[:[], :each, :etc...].all? { |m| obj.respond_to? m } ? obj : [obj]
The advantage is that you give the object a chance to implement it's own semantics for indexed access.

I do this a lot, and always use:
arg = [arg] unless arg.is_a?(Array)
Though if you know you're never passing in arrays as individual arguments you can also do:
arg = [arg].flatten

I'm not sure if this helps, but what I often need is not that the arg be an array, but that the arg responds to each.
arg = [arg] unless arg.respond_to? :each

What about Array.new(1, arg)?

Related

Can you pass arguments to the method in `&method(:method_ name)` in ruby? [duplicate]

I am aware of the shorthand for map that looks like:
[1, 2, 3, 4].map(&:to_s)
> ["1", "2", "3", "4"]
I was told this is shorthand for:
[1, 2, 3, 4].map{|i| i.to_s}
This makes perfect sense. My question is this: It seems there should be an easier way to write:
[1, 2, 3, 4].map{|x| f.call(x)}
for some procedure f. I know the way I just typed isn't all that long to begin with, but I'd contend that neither is the previous example for which the shorthand exists. This example just seems like the complement to the first example: Rather than calling i's to_s method for every i, I wish to call f for every x.
Does such a shorthand exist?
Unfortunately this shorthand notation (which calls "Symbol#to_proc") does not have a way to pass arguments to the method or block being called, so you couldn't even do the following:
array_of_strings.map(&:include, 'l') #=> this would fail
BUT, you are in luck because you don't actually need this shortcut to do what you are trying to do. The ampersand can convert a Proc or Lambda into a block, and vice-versa:
my_routine = Proc.new { |str| str.upcase }
%w{ one two three }.map &my_routine #=> ['ONE', 'TWO', 'THREE']
Note the lack of the colon before my_routine. This is because we don't want to convert the :my_routine symbol into a proc by finding the method and calling .method on it, rather we want to convert the my_routine Proc into a block and pass it to map.
Knowing this, you can even map a native Ruby method:
%w{ one two three }.map &method(:p)
The method method would take the p method and convert it into a Proc, and the & converts it into a block that gets passed to map. As a result, each item gets printed. This is the equivalent of this:
%w{ one two three }.map { |s| p s }
As of Ruby 2.7, numbered parameters (_1, _2, etc) are supported. This syntax can be cryptic, especially with more than one parameter, but it can be useful for simple situations.
[1, 2, 3, 4].map { f.call(_1) }

How do you get the class of an underlying object in a Ruby enumerable?

a = [4, 5, 3]
e = a.each
e.class #=> Enumerator
e.first.class #=> Fixnum
How do you find out if e is an enumerator for an array, hash or other type?
Working in Ruby 1.9.2
You can't (easily).*
Nor should you be able to. Enumerators aren't meant to care about what's contained within them, they're meant to iterate over something. You probably shouldn't be passing around enumerators anyway: just pass around the actual object.
Though, you could do nasty things like parse [].each.inspect with regex for either [] or {}, or the case where it's another type like #<Set: {}>. But this is just so horrible. I suggest re-thinking why you want to do this—and then not doing it.
* Note that the non-easy programmatic way would be to write C code using the Ruby C API and tap into the actual object pointer within the enumerator. This needs to be done because Enumerator is written in C, and causes doing things like [].each.instance_variables to return [].
Not that I propose that this is appropriate or in any way a good idea, but you CAN inspect an enumerator and potentially get some information that would give you a hint to the underlying object. This of course, begs the question of why you would want to do this...
a = [1,2,3,4]
=> [1, 2, 3, 4]
e = a.each
=> #<Enumerator: ...>
e.inspect
=> "<#Enumerator: [1, 2, 3, 4]:each>"
a = { :foo => "bar" }
=> {:foo=>"bar"}
e = a.each
=> #<Enumerator: ...>
e.inspect
=> "#<Enumerator: {:foo=>\"bar\"}:each>"
You can then use a regexp to try to tease out information about the underlying object. There are probably occasions for which this won't work (it does work for ranges). I'd emphasize again that there's probably no good reason to do this.
I would tentatively say that this can't be done. There doesn't seem to be any requirement, when creating an Enumerator, to actually reference the source object itself, only to tell the Enumerator how to get to the next, current, etc... values

Coercing a scalar or an array to become an array

Sometimes I want a variable to always be an array, whether its a scalar or already an array.
So I normally do:
[variable].flatten
which is compatible with ruby-1.8.5, 1.8.7, 1.9.x.
With this method when variable is a string (variable = "asdf"), it gives me ["asdf"]. If it's already an array (variable = ["asdf","bvcx"]), it gives me: ["asdf","bvcx"].
Does anyone have a better way? "Better" meaning more readable, more performant, succinct or more effective in other ways.
Array(variable)
should do the trick. It uses the little known Kernel#Array method.
The way I do, and think is the standard way, is using [*...]:
variable1 = "string"
variable2 = ["element1", "element2"]
[*variable1] #=> ["string"]
[*variable2] #=> ["element1", "element2"]
You might need something like Array.eat. Most other methods either call #to_a or #to_ary on the object. If you where using [ obj ].flatten that might give surprising results. #flatten will also mangle nested arrays unless called with a level parameter and will make an extra copy of the array.
Active support provides Array.wrap, but that also calls #to_ary, which might or might not be to your liking, depending on your needs.
require 'active_support/core_ext/array/wrap'
class Array
# Coerce an object to be an array. Any object that is not an array will become
# a single element array with object at index 0.
#
# coercing nil returns an empty array.
#
def self.eat( object )
object.nil? and return []
object.kind_of?( Array ) and return object
[object]
end
end # class Array
a = { a: 3 }
p [a].flatten # => [{:a=>3}]
p [*a] # => [[:a, 3]] -> OOPS
p Array a # => [[:a, 3]] -> OOPS
p Array.wrap a # => [{:a=>3}]
p Array.eat a # => [{:a=>3}]

Behaviour of Array bang methods

Some bang version of Array methods are like compact!, reject!, flatten!, uniq! return nil if no changes were made:
[1,[2]].flatten!
# => [1, 2]
[1,2].flatten!
# => nil
[1,[2]].flatten
# => [1, 2]
[1,2].flatten
# => [1, 2]
[1,2,nil].compact!
# => [1, 2]
[1,2].compact!
# => nil
[1,2,nil].compact
# => [1, 2]
[1,2].compact
# => [1, 2]
If they did it this way, there has to be a reason. Any ideas what it might be?
The bang (!) methods do modify the current object in place, but they do return nil if there are no affected elements per the documentation. This is useful if, for whatever reason, you need to do something if you did modify the array in question.
if array.flatten!
puts "Oh yeah... flattened that array!"
end
I was always under impression that
bang version of Array methods are
only different in the way that they
modify object in place.
Perhaps the problem here is that this impression is not really a correct one: according to David A. Black, ! does not mean that the method changes its receiver; ! means that this method is the "dangerous" version of an otherwise equivalent method, which has the same name minus the !.
Now danger takes many forms (emphasis mine):
Sometimes you get more than one kind
of "danger" even within one bang
method. Take String#gsub!. This
method changes its receiver:
str = "David"
str.gsub!(/$/, " Black")
str # David Black
It also differs from gsub (non-bang)
in that if the string does not change,
gsub returns a copy of the unchanged
string but gsub! returns nil:
str.gsub(/xyz/, "blah") # David Black
str.gsub!(/xyz/, "blah") # nil
str # David Black
The ! in gsub! gives you a heads-up:
it warns you of danger, and that means
that before you use the method, you
should find out exactly how it
behaves. (A simple "ri String#gsub!"
should do it.)
This "heads-up" semantics also applies to the bang methods of Array.

How to find a min/max with Ruby

I want to use min(5,10), or Math.max(4,7). Are there functions to this effect in Ruby?
You can do
[5, 10].min
or
[4, 7].max
They come from the Enumerable module, so anything that includes Enumerable will have those methods available.
v2.4 introduces own Array#min and Array#max, which are way faster than Enumerable's methods because they skip calling #each.
#nicholasklick mentions another option, Enumerable#minmax, but this time returning an array of [min, max].
[4, 5, 7, 10].minmax
=> [4, 10]
You can use
[5,10].min
or
[4,7].max
It's a method for Arrays.
All those results generate garbage in a zealous attempt to handle more than two arguments. I'd be curious to see how they perform compared to good 'ol:
def max (a,b)
a>b ? a : b
end
which is, by-the-way, my official answer to your question.
If you need to find the max/min of a hash, you can use #max_by or #min_by
people = {'joe' => 21, 'bill' => 35, 'sally' => 24}
people.min_by { |name, age| age } #=> ["joe", 21]
people.max_by { |name, age| age } #=> ["bill", 35]
In addition to the provided answers, if you want to convert Enumerable#max into a max method that can call a variable number or arguments, like in some other programming languages, you could write:
def max(*values)
values.max
end
Output:
max(7, 1234, 9, -78, 156)
=> 1234
This abuses the properties of the splat operator to create an array object containing all the arguments provided, or an empty array object if no arguments were provided. In the latter case, the method will return nil, since calling Enumerable#max on an empty array object returns nil.
If you want to define this method on the Math module, this should do the trick:
module Math
def self.max(*values)
values.max
end
end
Note that Enumerable.max is, at least, two times slower compared to the ternary operator (?:). See Dave Morse's answer for a simpler and faster method.

Resources