Creating a gsub! method for Arrays

D

Derek Cannon

I'm new to Ruby. I'm trying to add a method to the Array class that adds
the functionality of gsub! to arrays.

Looking around the internet, I found a keyword know as detect { } which
allows me to get the elements of the array. Testing it, I find the
following code:

class Array
def test
detect { |x| puts x}
end
end

...prints out all the elements in the array. However, when I try:

class Array
def gsub!(pattern, replacement)
detect { |x|
x.gsub!(pattern, replacement)
}
end
end

...the result is only a modification of the first element in the array.
For example, the following code:

x = ["Hello", "there", "world", "how", "are", "you?"]
x.gsub!(/[aeiou]/, "_")
puts x

outputs only:
H_ll_
there
world
how
are
you?

Does anyone know what I'm doing wrong?
 
D

Derek Cannon

After playing around some more, I've found a solution.

For anyone who is interested, the following code works:

class Array
def gsub!(pattern, replacement)
each { |x|
x.gsub!(pattern, replacement)
}
end
end
 
J

Jesús Gabriel y Galán

I'm new to Ruby. I'm trying to add a method to the Array class that adds
the functionality of gsub! to arrays.

Looking around the internet, I found a keyword know as detect { } which
allows me to get the elements of the array. Testing it, I find the
following code:

class Array
=A0def test
=A0 =A0detect { |x| puts x}
=A0end
end

...prints out all the elements in the array. However, when I try:

class Array
=A0def gsub!(pattern, replacement)
=A0 =A0detect { |x|
=A0 =A0 =A0x.gsub!(pattern, replacement)
=A0 =A0 =A0}
=A0end
end

...the result is only a modification of the first element in the array.
For example, the following code:

x =3D ["Hello", "there", "world", "how", "are", "you?"]
x.gsub!(/[aeiou]/, "_")
puts x

outputs only:
H_ll_
there
world
how
are
you?

Does anyone know what I'm doing wrong?

http://ruby-doc.org/core/classes/Enumerable.html#M003123

detect (Iprefer find) will iterate the enumerable yielding each
element to the block until one returns true, and will return that
element:

[1,2,3,4].find {|i| i > 2} # =3D> 3

In the first example you are calling puts in the block which always
returns null, so the detect never stops its iteration and it goes
through all the elements.

In your "real" code, you use gsub! in the block. gsub! returns the
modified string if it changed it, or nil if no replacement was made,
so you saw in your example that was stopping after the first element,
because it managed to change it with the replacement. As you have
discovered, you should use each to iterate through the full
enumerable.

Jesus.
 
R

Robert Klemme

2010/3/26 Derek Cannon said:
After playing around some more, I've found a solution.

For anyone who is interested, the following code works:

class Array
=A0def gsub!(pattern, replacement)
=A0 =A0each { |x|
=A0 =A0 =A0x.gsub!(pattern, replacement)
=A0 =A0 =A0}
=A0end
end

What is the benefit of doing

arr.gsub! /foo/, 'bar'

over

arr.each {|s| s.gsub! /foo/, 'bar'}

? Frankly I'd rather use the latter form. Because otherwise you'll
have to put a method in Array (or Enumerable for that matter) for
*every* operation you want to apply to elements. Additionally, there
is another point: since you made Array#gsub! look like String#gsub!
this might confuse readers of the code - especially if someone picks
bad variable names like "a".

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
I

Intransition

What is the benefit of doing

arr.gsub! /foo/, 'bar'

over

arr.each {|s| s.gsub! /foo/, 'bar'}

? =A0Frankly I'd rather use the latter form. =A0Because otherwise you'll
have to put a method in Array (or Enumerable for that matter) for
*every* operation you want to apply to elements. =A0Additionally, there
is another point: since you made Array#gsub! look like String#gsub!
this might confuse readers of the code - especially if someone picks
bad variable names like "a".

Agreed. There's also

require 'facets/enumerable/every'

arr.every.gsub! /foo/, 'bar'
 
D

Derek Cannon

Derek said:
class Array
def gsub!(pattern, replacement)
each { |x|
x.gsub!(pattern, replacement)
}
end
end

Just out of curiosity, does anyone know how Ruby knows what "each" is
referring to in this case? It works as intended -- I'm just curious as
to HOW.
 
J

Justin Collins

Derek said:
Just out of curiosity, does anyone know how Ruby knows what "each" is
referring to in this case? It works as intended -- I'm just curious as
to HOW.

If I understand your question correctly, "each" is being called on self,
which will be an instance of Array. "each" is already defined for Array,
and "gsub!" is just being added to the class.

In the example below, you expect meth2 to be able to call meth1. It is
analogous to the above.

class A
def meth1
puts "hi"
end

def meth2
meth1
end
end

A.new.meth2


-Justin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,709
Latest member
AustinMudi

Latest Threads

Top