Shawksta
Shawksta

Reputation: 67

Compressing a given String problem in Ruby

I have been trying to solve this problem for a while now, im supposed to take a given string like "aaabbc" and compress it into a new string that states multiples of a letter in a row in place. So it would output "3a2bc"

So far i managed to print it out except it counts all instances of a letter and im not sure how to get rid of the current repeats:

def compress_str(str)

    new_str = []
    word = str.split("")

    word.each do |char|
        count = 0
        word.each do |ele|
            if ele == char
                count += 1
            end
        end
        if count > 1
          new_str << count
          new_str << char
        else
          new_str << char
        end
    end

    return new_str.join("")

Example output:

Sample

Any suggestions on how I'm supposed to get rid of them?

Upvotes: 0

Views: 437

Answers (3)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

String#scan can also be handy here.

uncompressed = %w(aaabbcddaaaaa aaabb 111ddttttabaaacc)
uncompressed.map { |w| w.scan(/(.)(\1*)/).map(&:join) }
#⇒ [["aaa", "bb", "c", "dd", "aaaaa"],
#   ["aaa", "bb"],
#   ["111", "dd", "tttt", "a", "b", "aaa", "cc"]]

And to get the desired outcome.

uncompressed.map do |w|
  w.scan(/(.)(\1*)/).map(&:join).map do |l|
    "#{l.length}#{l[0]}"
  end.join
end
#⇒ ["3a2b1c2d5a", "3a2b", "312d4t1a1b3a2c"]

Upvotes: 1

iGian
iGian

Reputation: 11193

You could use String#chars (1), so Enumerable#chunk_while (2), then Enumerable#flat_map (3) into the desired format and finally Array#join:

str = "aaabbcaa"

str.chars.chunk_while { |x, y| x == y }.flat_map { |e| [(e.size unless e.size == 1), e.first] }.join
#=> "3a2bc2a"

Step by step

# (1)
str.chars#.to_a
#=> ["a", "a", "a", "b", "b", "c", "a", "a"]

so

# (2)
str.chars.chunk_while { |x, y| x == y }#.to_a
#=> [["a", "a", "a"], ["b", "b"], ["c"], ["a", "a"]]

then

# (3)
str.chars.chunk_while { |x, y| x == y }.flat_map { |e| [(e.size unless e.size == 1),e.first] }
#=> [3, "a", 2, "b", nil, "c", 2, "a"]

Upvotes: 1

marmeladze
marmeladze

Reputation: 6572

Using Enumerable#chunk might be a good fit for your needs.

uncompressed = %w(aaabbcddaaaaa aaabb 111ddttttabaaacc)

uncompressed.each do |str|
  puts str.chars.chunk{|e| e}.map {|e| "#{e[1].length}#{e[0]}"}.join
end

>>> 3a2b1c2d5a
>>> 3a2b
>>> 312d4t1a1b3a2c

Sure, you can add another check inside map block, so omit 1 before a single element and print as is.

Upvotes: 1

Related Questions