Behaviour of Array bang methods - ruby

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.

Related

Couldn't understand the difference between `puts{}.class` and `puts({}.class)`

As the anonymous block and hash block looks like approximately same. I was doing kind of playing with it. And doing do I reached to some serious observations as below:
{}.class
#=> Hash
Okay,It's cool. empty block is considered as Hash.
print{}.class
#=> NilClass
puts {}.class
#=> NilClass
Now why the above code showing the same as NilClass,but the below code shows the Hash again ?
puts ({}.class)
#Hash
#=> nil
print({}.class)
#Hash=> nil
Could anyone help me here to understand that what's going one above?
I completely disagree with the point of #Lindydancer
How would you explain the below lines:
print {}.class
#NilClass
print [].class
#Array=> nil
print (1..2).class
#Range=> nil
Why not the same with the below print [].class and print (1..2).class?
EDIT
When ambiguity happens with local variable and method call, Ruby throws an error about the fact as below :
name
#NameError: undefined local variable or method `name' for main:Object
# from (irb):1
# from C:/Ruby193/bin/irb:12:in `<main>'
Now not the same happens with {} (as there is also an ambiguity between empty code block or Hash block). As IRB also here not sure if it's a empty block or Hash. Then why the error didn't throw up when IRB encountered print {}.class or {}.class?
The precedence rules of ruby makes print{}.class interpreted as (print{}).class. As print apparently returns a nil the class method returns #NilClass.
EDIT: As been discussed on other answers and in the updates to the question, print{} it of course interpreted as calling print with a block, not a hash. However, this is still about precedence as {} binds stronger than [] and (1..2) (and stronger than do ... end for that matter).
{} in this case is recognized as block passed to print, while [] unambiguously means empty array.
print {}.class # => NilClass
print do;end.class # => NilClass
You are running into some nuances of Ruby, where characters mean different things depending on context. How the source code is interpreted follows rules, one of which is that {} is a closure block if it follows a method call, and otherwise a Hash constructor.
It's common throughout the language to see characters mean different things depending on context or position within the statement.
Examples:
Parens () used for method call or for precedence
print(1..5).class => NilClass
print (1..5).class => Range <returns nil>
Square brackets [] used to call :[] method or for Array
print[].class => NoMethodError: undefined method `[]' for nil:NilClass
print([].class) => Array <returns nil>
Asterisk * used for multiplication or splatting
1 * 5 => 5
[*1..5] => [1, 2, 3, 4, 5]
Ampersand & used for symbol -> proc or logical and
0 & 1 => 0
[1, 2, 3].map(&:to_s) => ["1", "2", "3"]
Or in your case, braces used for block closures or for a hash
... hope it makes sense now ...

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}]

Ruby: How to loop through an object that may or may not be an array?

I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.

Ruby: Object.to_a replacement

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)?

Resources