Search code examples
rubycharchaining

Ruby: Chaining methods give different results: .chars.pop


I want to make an iteration loop where at each step the string "***" would have a sign disappearing (iteration 2 : "**", iteration 3: "*"). I tried in irb different ways to do so and results seems to be all over the place !

[14] pry(main)> a="***".chars.pop
=> "*"
[15] pry(main)> a
=> "*"
[16] pry(main)> a="***".chars
=> ["*", "*", "*"]
[17] pry(main)> a.pop
=> "*"
[18] pry(main)> a
=> ["*", "*"]
[19] pry(main)> a="***"
=> "***"
[20] pry(main)> a.chars.pop
=> "*"
[21] pry(main)> a
=> "***"

So sometimes I have "*", sometimes ["*","*"], or even "***"... What the heck ? could you please explain what is happening here and what would be the best way to achieve what I'm trying to do? Thanks a million!


Solution

  • a = "***".chars.pop
    # => "*"
    a
    # => "*"
    

    Here, you take the string "***", and gather an array of its characters with "***".chars. The array of characters (i.e. one-character strings) is in no way connected to the original string.

    Then, you pop the last element from the character array. Array#pop now modofies the original array (to remove the last element) and returns this element. Since you are assigning the result of this method to your a variable, you effectively set a = "*". The intermediate array of characters as well as the initial string are thrown away.

    a = "***".chars
    # => ["*", "*", "*"]
    

    Here, you assign the array of characters to the a variable (and throw away the initial string).

    a.pop
    # => "*"
    

    As explained above, Array#pop return the last element of the array...

    a
    # => ["*", "*"]
    

    ... and it modifies the array it is called on to remove the last element (i.e. the one returned). Thus, a now has only 2 elements, all but the last one poped off.

    a = "***"
    # => "***"
    

    Here, you assign a string "***" to the a variabe.

    a.chars.pop
    # => "*"
    

    As before, you get an array of chartacters from the string (which again is not connected to the original string in any way) and remove the last element which is returned. The intermediate array returned by a.chars is thrown away.

    a
    # => "***"
    

    Since you have only modified the intermediate array but not the original string, a is unchanged.


    Now, it apears that you intially want to just remove the last character of a string. This can be done by:

    a = "***"
    b = a[0,-2]
    

    Here, you get a new string b which is basically a copy of the string a with the last character removed.

    If you want to modify the original string in place (which often is not desireable), you could e.g. use:

    a = "***"
    a.chop!
    a
    # => "**"
    

    (Note though that Array#chop! also returns a trailing newline if present. See the documentation for details and note the difference between String#chop and String#chop!.