Andriy Ozorovych
Andriy Ozorovych

Reputation: 3

Can't get updated values in array after using .map method

I need to implement a method, which works that way:

# do_magic("abcd") # "Aaaa-Bbb-Cc-D"
# do_magic("a")    # "A"
# do_magic("ab")   # "Aa-B"
# do_magic("teSt") # "Tttt-Eee-Ss-T"

My decision was to convert a string into an array, iterate through this array and save the result. The code works properly inside the block, but I'm unable to get the array with updated values with this solution, it returns the same string divided by a dash (for example "t-e-S-t" when ".map" used or "3-2-1-0" when ".map!" used):

def do_magic(str)
  letters = str.split ''
  counter = letters.length
  while counter > 0
    letters.map! do |letter|
      (letter * counter).capitalize
      counter -= 1
    end
  end
  puts letters.join('-')
end

Where is the mistake?

Upvotes: 0

Views: 59

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110725

I suggest the following.

def do_magic(letters)
  length = letters.size
  letters.downcase.each_char.with_index.with_object([]) { |(letter, i), new_letters|
    new_letters << (letter * (length - i)).capitalize }.join
end

do_magic 'teSt'
  # => "TtttEeeSsT"

Let's go through the steps.

letters = 'teSt'

length = letters.size
  #=> 4
str = letters.downcase
  #=> "test"
enum0 = str.each_char
  #=> #<Enumerator: "test":each_char>
enum1 = enum0.with_index
  #=> #<Enumerator: #<Enumerator: "test":each_char>:with_index>
enum2 = enum1.with_object([])
  #=> #<Enumerator: #<Enumerator: #<Enumerator: "test":each_char>:
  #     with_index>:with_object([])>

Carefully examine the return values from the creation of the enumerators enum0, enum1 and enum2. The latter two may be thought of as compound enumerators.

The first element is generated by enum2 (the value of enum2.next) and the block variables are assigned values using disambiguation (aka decomposition).

(letter, i), new_letters = enum2.next
  #=> [["t", 0], []]
letter
  #=> "t"
i #=> 0
new_letters
  #=> []

The block calculation is then performed.

m = letter * (length - i)
  #=> "tttt"
n = m.capitalize
  #=> "Tttt"
new_letters << n
  #=> ["Tttt"]

The next element is generated by enum2, passed to the block and the block calculations are performed.

(letter, i), new_letters = enum2.next
  #=> [["e", 1], ["Tttt"]]
letter
  #=> "e"
i #=> 1
new_letters
  #=> ["Tttt"]

Notice how new_letters has been updated. The block calculation is as follows.

m = letter * (length - i)
  #=> "eee"
n = m.capitalize
  #=> "Eee"
new_letters << n
  #=> ["Tttt", "Eee"]

After the last two elements of enum2 are generated we have

new_letters
  #=> ["Tttt", "Eee", "Se", "T"]

The last step is to combine the elements of new_letters to form a single string.

new_letters.join
  #=> "TtttEeeSeT"

Upvotes: 0

Nandu Kalidindi
Nandu Kalidindi

Reputation: 6280

You can try something like this using each_with_index

def do_magic(str)
    letters = str.split("")
    length = letters.length
    new_letters = []
    letters.each_with_index do |letter, i|
        new_letters << (letter * (length - i)).capitalize
    end
    new_letters.join("-")
end

OR

using map_with_index equivalent each_with_index.map

def do_magic(str)
    letters = str.split("")
    length = letters.length
    letters.each_with_index.map { |letter, i| 
       (letter * (length - i)).capitalize 
    }.join("-")
end

Upvotes: 1

Josh Brody
Josh Brody

Reputation: 5363

You're so close. When you have a block (letters.map!), the return of that block is the last evaluated statement. In this case, counter -= 1 is being mapped into letters.

Try

l = (letter * counter).capitalize
counter -= 1
l 

Upvotes: 2

Related Questions