Behaviour of Array Bang Methods

Behaviour of Array bang methods

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

Why Array#slice and Array#slice! behave differently?

#slice and #slice! behaviors are equivalent: both "return a subarray starting at the start index and continuing for length elements", the same way as #sort and #sort! return a sorted array or #reverse and #reverse! return a reversed array.

The difference is that the bang methods also modify the object itself.

a = [4,2,6,9,1,5,8]
b = a.dup
a.sort == b.sort! # => true
a == b # => false

b = a.dup
a.reverse == b.reverse! # => true
a == b # => false

b = a.dup
a.slice(2,2) == b.slice!(2,2) # => true
a == b # => false

Where to put exclamation mark when methods are chained

Exclamation marks are not modifiers.

array.reject!(&:blank?).map!(&:to_i).reject!{|i| i == 0}

However, this code is subtly wrong. From reject! docs:

returns nil if no changes were made.

Whoops! This could break your whole chain. Instead, you're supposed to use delete_if, which always returns the array.

array.delete_if(&:blank?).map!(&:to_i).delete_if{|i| i == 0}

Yes, it's confusing it doesn't have a bang, but it does modify in-place. From the docs:

The array is changed instantly every time the block is called, not after the iteration is over.

.all?' method's strange behaviour

Because Enumerable#all? says :

If the block is not given, Ruby adds an implicit block of { |obj| obj } which will cause all? to return true when none of the collection members are false or nil.

In your second line,the code return true as none of the collection members are false or nil

In your third line code returns true ,as all objects in your code collection (0,1) are true,as in Ruby all objects are true except nil and false.

In your first line,the code return false,as the collection contains false values along with true values.

Shouldn't force_encoding be force_encoding! in ruby?

Contrary to popular belief, and many misleading beginners' ruby guides, bang methods do not mean "this method mutates the object".

That's usually the case, but not always.

A better interpretation of the convention for bang methods is "this is a more dangerous version of its non-bang counterpart".

For example:

  • String#gsub! is the "dangerous" version of String#gsub, since it mutates the object.
  • ActiveRecord::Base#save! is the "dangerous" version of ActiveRecord::Base#save, since it raises an exception if validation fails.
  • exit! is the "dangerous" version of exit, since the former exits immediately, whereas the latter actually raises an exception - which could be rescued elsewhere in the code.

Furthermore, note that there are plenty of ruby methods that do mutate the object but aren't bang methods. For example, String#delete and Array#pop.

This article goes into more detail on the matter.


So, (warning: opinionated response!) I would argue that:

  1. It's fine for String#force_encoding to be a non-bang method. This is not completely inconsistent with other conventions in the language.
  2. If it were a bang method, it would require a non-bang counterpart for consistency.
  3. I, personally, don't think the use case for a non-mutating String#force_encoding is common enough to warrant its creation. In the rare case that you actually wanted to do this, you could write: string.dup.force_encoding(...).

.all?' method's strange behaviour

Because Enumerable#all? says :

If the block is not given, Ruby adds an implicit block of { |obj| obj } which will cause all? to return true when none of the collection members are false or nil.

In your second line,the code return true as none of the collection members are false or nil

In your third line code returns true ,as all objects in your code collection (0,1) are true,as in Ruby all objects are true except nil and false.

In your first line,the code return false,as the collection contains false values along with true values.

Ruby !sub returning strange null

It is documented that the sub! (like many of the ! string operations) return nil if no change was made.

From the docs

Performs the substitutions of String#sub in place, returning str, or nil if no substitutions were performed.

Instead use the regular sub. In your case the extra bang (!) is unnecessary.

'fafeafeafewafeawfeaw'.sub(/\.[^\.]*$/, '')

Bang Methods

The difference between sub and sub! is subtle. But in ruby in general, the non bang (!) version of a method is safer. Since by convention the bang means the method has more side affects.

In the case of string functions (and many array/enumerable functions) the bang means the method operates on the contents of the caller, instead of making (and returning) a copy.

s = 'fafafa'
puts s #=> 'fafafa'

puts s.sub(/fa/, 'fo') #=> 'fofofo'

puts s #=> 'fafafa'

puts s.sub!(/fa/, 'fo') #=> 'fofofo'

puts s #=> 'fofofo'

Kotlin spread operator behaviour on chars array

This happens because in Kotlin Array<Char> is equal to Character[] in Java, not to char[] in Java.

To use the spread operator on an array of characters and pass it to a vararg Char parameter, you need to use CharArray which is equal to char[] in Java.

fun main() {
val strings = arrayOf("one", "two")

val stringSplit = "".split("one", "two")
val stringsSplit = "".split(*strings)

val chars = charArrayOf('1', '2')

val charSplit = "".split('1', '2')
val charsSplit = "".split(*chars) // this is not possible
}


Related Topics



Leave a reply



Submit