2

I'm trying to turn the first letters of a string into uppercase characters. I am trying to turn:

"this is a test sentence"

into:

"This Is A Test Sentence"

Here is my code:

def first_upper(str)
  arr_str = str.split
  arr_str.map do |item|
    item.capitalize
  end
  arr_str = arr_str.join(' ')
  puts arr_str
end

first_upper('this is a test sentence')
# >> this is a test sentence

item.capitalize works inside the map loop if I put pry's binding.pry there.

Am I not using the map method correctly?

sawa
  • 165,429
  • 45
  • 277
  • 381
user4396386
  • 423
  • 8
  • 15

3 Answers3

3

The ´map´ method does not mutate it's receiver, instead it returns a new modified copy of it. You can either use it's mutative/destructive counterpart map! or just reassign the value of the expression to the variable.

arr_str.map! do |item|
  item.capitalize
end

# ... or ...

arr_str = arr_str.map do |item|
  item.capitalize
end
ichigolas
  • 7,595
  • 27
  • 50
  • 1
    There's a third option: mutate the strings themselves, with `item.capitalize!`. (Although, if you're not sure where things are reused, the non-destructive `arr_str = ...` way is the safest.) – Amadan Aug 30 '18 at 02:34
2

It "doesn't work" as you expect to do it. As capitalize applies the capitalize method on item, the receiver, and then returns a new representation of your arr_str yielding what's inside the block. In other words and as the doc states:

map { |obj| block } → array click to toggle source
map → an_enumerator
Returns a new array with the results of running block once for every element in enum.

Your arr_str would be modified if you use a "persistent" method to do so, like the map's brother, Array#map!, which works slightly different:

map! {|item| block } → ary click to toggle source
map! → Enumerator
Invokes the given block once for each element of self, replacing the element with the value returned by the block.

What your code is doing right now, is spliting the string passed, iterating over each element within the generated and stored result of split, and applying capitalize on those elements, and finally joining them and printing them with puts:

def first_upper(str)
  arr_str = str.split         # ["this", "is", "a", "test", "sentence"]

  arr_str.map do |item|
    item.capitalize
  end                         # ["This", "Is", "A", "Test", "Sentence"]

  arr_str = arr_str.join(' ') # this is a test sentence
  puts arr_str                # this is a test sentence
end

first_upper('this is a test sentence')

The most simple and evident approaches are:

A) To store the result of arr_str.map in a variable to be used within the function after that step.

B) To use map! and this way modify arr_str and "persists" what's being done within that block.

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
0

Just to add my two cents..

These are some ways, monkey patched into String class.

module MyStringPatch

  def titleize_1
    split(' ').collect(&:capitalize).join(' ') # Stolen from user3869936
  end

  def titleize_2
    gsub(/\b(?<!['â`])[a-z]/) { |match| match.capitalize } # Stolen from Rails
  end

 def titleize_3
   split.map { |item| item.capitalize }.join(' ') # Stolen from OP: user4396386
 end

end

String.include MyStringPatch

string = "this is a test sentence"

p string.titleize_1 # => "This Is A Test Sentence"
p string.titleize_2 # => "This Is A Test Sentence"
p string.titleize_3 # => "This Is A Test Sentence"

Credits:

iGian
  • 11,023
  • 3
  • 21
  • 36