1

I want to replace a character in string when a particular condition is satisfied. So , I went through the API doc of Ruby and found the gsub , gsub! etc for similar purpose. When I implemented that in my program I didn't got any error but din't got the desired output too.

The code which I was trying is this :

name.each_char  { |c|

if name[c] == "a"
    name.sub( name[c] , c )
    puts "matched....   "
end

So , for example , I have a string called huzefa and want to replace all the letters with its index numbers . So , what is the way to do it ? Please explain in detail by giving a simple example.

Shiva
  • 11,485
  • 2
  • 67
  • 84
  • what is the reason to downvote ? please explain for clearification . – huzefa biyawarwala Oct 25 '15 at 13:15
  • 1
    may be they think its simple to be asked; or the title smells duplication or there may be other reasons. or the first `downvote` might have triggered/influenced the second downvote. (I didnot downvote though, rather upvoted) – Shiva Oct 25 '15 at 13:32
  • @illusionist : thank you for your edit. And yes that may be the reason. – huzefa biyawarwala Oct 25 '15 at 13:36
  • Converting each character of a string to the string representation of its index depends only on the length of the string: `len = 'huzefa'.size; (0...len).map(&:to_s).join # "012345"`. The result is the same for every other six-character string. – Cary Swoveland Oct 25 '15 at 16:26
  • Your suggestion is very proper for the case if my requirement was to convert all . But i just want to map only those char to index which satisfies my particular condition . But , still i got to know something new from your answer . Thank you. :) – huzefa biyawarwala Oct 25 '15 at 16:43
  • You said, "...and want to replace all the letters with its index numbers". If a condition needs to be satisfied you need to say so. Also, `name.sub( name[c] , c )` returns `name` with `name[c]` replaced by `c`, but (even though `name[c]==c`) that does not alter `name`, because the returned string is not captured by a variable. It therefore has no effect and is merely garbage-collected. – Cary Swoveland Oct 25 '15 at 18:28

2 Answers2

3

You could pass block to gsub and do whatever you want when match happend.

To do it inplace you could use gsub! method.

name = "Amanda"
new_name = name.gsub("a") do |letter|
  puts "I've met letter: " + letter
  "*"
end
# I've met letter: a
# I've met letter: a
# => "Am*nd*"

If you want to work with indexes, you could do something like this:

new_name = name.chars.map.with_index do |c, i|
  if i.odd?
    "*"
  else
    c
  end
end.join
#=> => "A*a*d*"

Here c and i are passed to the block. c is a character and i is an index.

fl00r
  • 82,987
  • 33
  • 217
  • 237
  • Thank you , your second suggestion worked for me. If this is not stupid to ask , how does that `end.join` works ? . Thanx :) – huzefa biyawarwala Oct 25 '15 at 14:15
  • Huzefa, the block returns an array of (one-character) strings. [Array#join](http://docs.ruby-lang.org/en/2.0.0/Array.html#method-i-join) combines those elements of the array into a single string. – Cary Swoveland Oct 25 '15 at 15:43
  • 1
    Good answer. A small suggestion: whenever the characters of a string are to be food for an enumerable, use `each_char`, rather than `chars`, to avoid the creation of an unneeded temporary array. On a different subject, you've had those eyeglasses for some time. I'm compelled to tell you that they've fallen out of style. – Cary Swoveland Oct 25 '15 at 16:41
  • @CarySwoveland nice catch – fl00r Oct 25 '15 at 18:01
  • @CarySwoveland : So , you mean to say we should just replace `chars` with `each_char` , or to iterate through a loop using `each_char`? I am just asking for more understanding of the suggestion given by you. – huzefa biyawarwala Oct 26 '15 at 04:48
  • 1
    Suppose you want to return a string that is the same as `'cat'` except with the letter `"a"` capitalized. If you write `'cat'.chars.map { |c| c=='a' ? 'A' : c }.join #=> "cAt"`, the array `arr = 'cat'.chars #=> ["c", "a", "t"]` is first constructed, then `arr.map { |c| c=='a' ? 'A' : c }.join #=> "cAt"` is executed. This works because the class `Array` `include`s `Enumerable` and implements `Array#each`. (cont...) – Cary Swoveland Oct 26 '15 at 07:06
  • 1
    By contrast, if you write `'cat'.each_char.map { |c| c=='a' ? 'A' : c }.join #=> "cAt"`, the first step is to create an enumerator `enum = 'cat'.each_char #=> #`, then `enum.map { |c| c=='a' ? 'A' : c }.join #=> "cAt"`. This works because `enum` is an instance of the class `Enumerator`, which `include`s `Enumerable` and implements the method `each`. (We can see the elements `enum` will pass to `Enumerable#map` by converting `enum` to an array: `enum.to_a #=> ["c", "a", "t"]`.) (cont...) – Cary Swoveland Oct 26 '15 at 07:16
  • 1
    Think of an enumerator as a rule that acts on its receiver (here a string). Being a rule, it requires less memory than does `chars`, which creates a temporary array (if the string is more than a few characters anyway). On the other hand, suppose we wanted to return a string that shuffles the letters in `"cat"`. Here we *must* write `"cat".chars.shuffle.join #=> "atc"` because `shuffle` is an instance method of `Array`. If we tried using `each_char` we get `"cat".each_char.shuffle.join #=> undefined method 'shuffle' for #`. – Cary Swoveland Oct 26 '15 at 07:25
  • @CarySwoveland : I wouldn't have got a better explanation then the above given by you. I am very clear regarding the `chars` and `each_char` now . Thank you :) – huzefa biyawarwala Oct 27 '15 at 04:08
3

if name=huzefa and you want to replace 'a' with its index..

name.split(//).map.with_index{|x,y| (x=='a')? y : x}.join

to result in #> "huzef5"

owade
  • 306
  • 1
  • 3
  • 9