I would like to offer two solutions. Neither mutates the array that is passed to the method as an argument.
arr = ["apple", "banana", "pineapple", "plum", "pear"]
#1
def to_sentence(arr)
*all_but_last, last = arr.shuffle
all_but_last.join(', ') << ' and ' << last
end
to_sentence(arr)
#=> "pear, plum, pineapple, apple and banana"
to_sentence(arr)
#=> "banana, pear, pineapple, plum and apple"
to_sentence(arr)
#=> "banana, plum, apple, pineapple and pear"
to_sentence(arr)
#=> "banana, pineapple, pear, apple and plum"
#2
def to_sentence(arr)
arr.shuffle.join(', ').sub(/,(?!.*,)/, ' and')
end
to_sentence(arr)
#=> "banana, plum, pear, apple and pineapple"
to_sentence(arr)
#=> "pear, plum, pineapple, banana and apple"
to_sentence(arr)
#=> "apple, pineapple, plum, pear and banana"
to_sentence(arr)
#=> "apple, plum, pear, pineapple and banana"
The regular expression matches a comma that is not followed later in the string by another comma (that is, it matches the last comma in the string). (?!.*,) is a negative lookahead.
The regular expression could also be written
/.*\K,/
.* matches any character other than line terminators, as many as possible (including commas), because * is by default greedy. \K then resets the start of the match to the current location in the string (right before the last comma) and discards any previously-consumed characters from the match that is returned. A comma is then matched, which necessarily is the last comma in the string.