Do something in middle of recursive function, then return as needed - ruby

How do I do something in the middle of a recursion, and return as needed? In other words, maybe no more recursion is needed because I have found a "solution" in which case to save resources, the recursion can stop.
For example, let's say I have a working permute method that does this
permute([["a","b"],[1,2]])
>>> [["a", 1], ["a", 2], ["b", 1], ["b", 2]]
Rather than have the method generate all 4 possibilities, if one meets my requirements, I'd like it to stop. For example, let's say I'm searching for ["a",2], then the method can stop after it creates the second possibility.
This is my current permute method that is working
def permute(arr)
if arr.length == 1
return arr.first
else
first = arr.shift
return first.product(permute(arr)).uniq
end
end
I feel like I need to inject a do block somewhere with something like the below, but not sure how/where...
if result_of_permutation_currently == ["a",2]
return ...
else
# continuing the permutations
end

You could write your method as follows.
def partial_product(arr, last_element)
#a = []
#last_element = last_element
recurse(arr)
#a
end
def recurse(arr, element = [])
first, *rest = arr
if rest.empty?
first.each do |e|
el = element + [e]
#a << el
return true if el == #last_element
end
else
first.each do |e|
rv = recurse(rest, element + [e])
return true if rv
end
end
false
end
arr = [["a","b"], [1,2,3], ["cat","dog"]]
partial_product(arr, ["b",2,"dog"])
#=> [["a", 1, "cat"], ["a", 1, "dog"], ["a", 2, "cat"],
# ["a", 2, "dog"], ["a", 3, "cat"], ["a", 3, "dog"],
# ["b", 1, "cat"], ["b", 1, "dog"], ["b", 2, "cat"],
# ["b", 2, "dog"]]
partial_product(arr, ["a",1,"dog"])
#=> [["a", 1, "cat"], ["a", 1, "dog"]]
partial_product(arr, ["b",2,"pig"])
#=> [["a", 1, "cat"], ["a", 1, "dog"], ["a", 2, "cat"],
# ["a", 2, "dog"], ["a", 3, "cat"], ["a", 3, "dog"],
# ["b", 1, "cat"], ["b", 1, "dog"], ["b", 2, "cat"],
# ["b", 2, "dog"], ["b", 3, "cat"], ["b", 3, "dog"]]
If you prefer to avoid using instance variables, you could carry a and last_element as arguments in recurse, but there would be inefficiencies by doing so, particularly in terms of memory use.

Here are two ways that could be done without using recursion.
Use each to generate elements of the desired array until the target pair is reached
def permute(arr1, arr2, last_pair = [])
arr1.each_with_object([]) do |e1,a|
arr2.each do |e2|
a << [e1, e2]
break a if [e1, e2] == last_pair
end
end
end
permute(["a","b"],[1,2],["b", 1])
#=> [["a", 1], ["a", 2], ["b", 1]]
permute(["a","b"],[1,2],["b", 99])
#=> [["a", 1], ["a", 2], ["b", 1], ["b", 2]]
permute(["a","b"],[1,2])
#=> [["a", 1], ["a", 2], ["b", 1], ["b", 2]]
permute(["a","b"],[],["b", 1])
#=> []
permute([],[1,2],["b", 1])
#=> []
permute([],[],["b", 1])
#=> []
Map a sequence of the indices of the desired array
def permute(arr1, arr2, last_pair = [])
n1 = arr1.size
n2 = arr2.size
idx1 = arr1.index(last_pair.first)
idx2 = idx1.nil? ? nil : arr2.index(last_pair.last)
return arr1.product(arr2) if idx2.nil?
0.step(to: idx1*n2+idx2).
map {|i| [arr1[(i % (n1*n2))/n2], arr2[i % n2]]}
end
permute(["a","b"],[1,2],["b", 1])
See Numeric#step
idx1*n2 + idx2, the number of elements in the array to be returned, is computed as follows.
last_pair = ["b", 1]
n2 = arr2.size
#=> 2
idx1 = arr1.index(last_pair.first)
#=> 1
idx2 = idx1.nil? ? nil : arr2.index(last_pair.last)
#=> 0
idx1*n2 + idx2
#=> 2
The element at index i of the array returned is:
n1 = arr1.size
#=> 2
[arr1[(i % (n1*n2))/n2], arr2[i % n2]]
#=> [["a","b"][(i % 2*2)/2], [1,2][i % 2]]
For i = 1 this is
[["a","b"][(1 % 4)/2], [1,2][1 % 2]]
#=> [["a","b"][0], [1,2][1]]
#=> [“a”, 2]
For i = 2 this is
[["a","b"][(2 % 4)/2], [1,2][2 % 2]]
#=> [["a","b"][1], [1,2][0]]
#=> [“b”,1]
Note that we cannot write
arr1.lazy.product(arr2).first(idx1*n2+idx2+1)
because arr1.lazy returns an enumerator (arr1.lazy
#=> #<Enumerator::Lazy: ["a", "b"]>) but Array#product requires it's receiver to be an array. It's for that reason that some Rubyists would like to see product made an Enumerable method (with a lazy version), but don't hold your breathe.

Related

How to find the coordinates of a specific matrix-entry in ruby

Say I have a matrix, 5×6, filled with single letters and a few special signs, like this one:
upper = [['A', 'B', 'C', 'D', 'E'],
['F', 'G', 'H', 'I', 'J'],
['K', 'L', 'M', 'N', 'O'],
['P', 'Q', 'R', 'S', 'T'],
['U', 'V', 'W', 'X', 'Y'],
['Z', ',', '.', ' ', '?']]
How can I determine the coordinates of single letters of a string I loop through?
I found no clear description on the #index method, and all ways I've tried to call it (i.e. upper.index("A") ), have failed.
In the end I'm trying to code a simple version of the two-square encryption method and this is the one step I am currently stumped on.
Thanks for your help!
You could build an array of coordinates:
coordinates = upper.first.each_index.to_a.product(upper.each_index.to_a)
#=> [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5],
# [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5],
# [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5],
# [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5],
# [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5]]
And find the item's coordinates by traversing this array:
coordinates.find { |x, y| upper[y][x] == 'A' }
#=> [0, 0]
coordinates.find { |x, y| upper[y][x] == '?' }
#=> [4, 5]
coordinates.find { |x, y| upper[y][x] == '-' }
#=> nil
Alternative approach
Instead of a (two-dimenstional) array of rows, you could use a (one-dimensional) hash of coordinate => value pairs. Example:
str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ,. ?'
upper = {}
str.each_char.with_index { |c, i| upper[i.divmod(5)] = c }
upper
#=> {[0, 0]=>"A", [0, 1]=>"B", [0, 2]=>"C", [0, 3]=>"D", [0, 4]=>"E",
# [1, 0]=>"F", [1, 1]=>"G", [1, 2]=>"H", [1, 3]=>"I", [1, 4]=>"J",
# [2, 0]=>"K", [2, 1]=>"L", [2, 2]=>"M", [2, 3]=>"N", [2, 4]=>"O",
# [3, 0]=>"P", [3, 1]=>"Q", [3, 2]=>"R", [3, 3]=>"S", [3, 4]=>"T",
# [4, 0]=>"U", [4, 1]=>"V", [4, 2]=>"W", [4, 3]=>"X", [4, 4]=>"Y",
# [5, 0]=>"Z", [5, 1]=>",", [5, 2]=>".", [5, 3]=>" ", [5, 4]=>"?"}
The values can be accessed by [x, y] coordinates:
upper[[0, 2]] #=> "C"
and looking up a key is trivial:
upper.key('C') #=> [0, 2]
You can also build an inverse hash that maps values to coordinates:
upper.invert
#=> {"A"=>[0, 0], "B"=>[0, 1], "C"=>[0, 2], "D"=>[0, 3], "E"=>[0, 4],
# "F"=>[1, 0], "G"=>[1, 1], "H"=>[1, 2], "I"=>[1, 3], "J"=>[1, 4],
# "K"=>[2, 0], "L"=>[2, 1], "M"=>[2, 2], "N"=>[2, 3], "O"=>[2, 4],
# "P"=>[3, 0], "Q"=>[3, 1], "R"=>[3, 2], "S"=>[3, 3], "T"=>[3, 4],
# "U"=>[4, 0], "V"=>[4, 1], "W"=>[4, 2], "X"=>[4, 3], "Y"=>[4, 4],
# "Z"=>[5, 0], ","=>[5, 1], "."=>[5, 2], " "=>[5, 3], "?"=>[5, 4]}
I would start with building hash letter ⇒ index:
hash = upper.each_with_index.inject({}) do |memo, (inner, x)|
inner.each_with_index.inject(memo) do |memo, (letter, y)|
memo[letter] = [x,y]
memo
end
end
Now we have a desired hash, so to determine an index:
▶ hash['C']
#⇒ [
# [0] 0,
# [1] 2
# ]
Since according to your question, you are going to iterate through string and find an index for each letter, it is way more efficient not to lookup for an index on every loop iteration. Once this hash is built, the lookup for indices will be as fast as hash lookup.
Will this work for you?
Assuming c represents a character whose index you are interested in, we loop through the 2-D array - and if a inner array contains c, then, we take its index in outer array, and index of c in that inner array, and assign them as array to value pos
upper=[['A','B','C','D','E'],['F','G','H','I','J'],
['K','L','M','N','O'],['P','Q','R','S','T'],
['U','V','W','X','Y'],['Z',',','.',' ','?']]
def find_index(array, c)
# initialize the return value to be empty
pos = []
# each_index is a method that will execute the given block (code inside {..}
# for each element of the array, by passing index as parameter to block.
# In this case 'i' receives index value.
# if array[i].include?(c) checks whether array[i] has an element with
# value contained in variable c. If yes, then, we lookup index of c in
# it by using array[i].index(c).
# We then use array literal notation, such as pos = [x,y], to assign the
# the result
array.each_index { |i| pos = [i, array[i].index(c)] if array[i].include?(c) }
# Return pos - you need not use 'return' keyword, last statement's value is treated as return value in Ruby
return pos
end
# p prints the variable value - in below cases prints the return value of function calls
p find_index(upper, 'C')
p find_index(upper, 't'.upcase)
p find_index(upper, 'S')
x, y = *find_index(upper, '?') # Here we splat the array into two variables
p x
p y
Output
[0, 2]
[3, 4]
[3, 3]
5
4
Here is the another variant of above function which purposefully stays away with advanced Ruby Idioms and tries to keep code easier to understand
def find_index(array, c)
x = nil, y = nil
# Find the index of array that has element c
for i in 0..array.length-1 do
if array[i].include?(c)
x = i
end
end
# Find the index of c within that array
y = array[x].index(c)
return [x, y]
end
The following is one way to do it:
upper = [['A','B','C','D','E','C'],
['F','C','C','C','G','H','I','J'],
['K','L','M','N','O'],
['P','Q','R','S','T'],
['U','V','W','X','Y','C'],
['Z',',','.',' ','?']
]
target = 'C'
upper.each_with_index.with_object([]) { |(a,i),arr|
a.each_with_index { |c,j| arr << [i,j] if c == target } }
#=> [[0, 2], [0, 5], [1, 1], [1, 2], [1, 3], [4, 5]]
Note that Array#index doesn't work when the target appears more than once in an inner array.
If the target can appear at most once in each inner array, you could write:
upper = [['A','B','C','D','E'],
['F','C','G','H','I','J'],
['K','L','M','N','O'],
['P','Q','R','S','T'],
['U','V','W','X','Y','C'],
['Z',',','.',' ','?']
]
target = 'C'
upper.each_with_index.with_object([]) do |(a,i),arr|
j = a.index(target)
arr << [i,j] if j
end
#=> [[0, 2], [1, 1], [4, 5]]
As you are a self-confessed Ruby newbie, this probably looks pretty formidable. It's not so bad, however, if we break it down step-by-step.
We first send the method Enumerable#each_with_index to the "receiver" upper, without a block. If you examine the docs for that method, you'll see that an enumerator is returned:
enum0 = upper.each_with_index
#=> #<Enumerator: [["A", "B", "C", "D", "E"], ["F", "C", "G", "H", "I", "J"],
# ["K", "L", "M", "N", "O"], ["P", "Q", "R", "S", "T"],
# ["U", "V", "W", "X", "Y", "C"],
# ["Z", ",", ".", " ", "?"]]:each_with_index>
Next, the method Enumerator#with_object is sent to enum0, with an argument equal to an empty array (the "object"):
enum1 = enum0.with_object([])
#=> #<Enumerator: #<Enumerator: [["A", "B", "C", "D", "E"],
# ["F", "C", "G", "H", "I", "J"], ["K", "L", "M", "N", "O"],
# ["P", "Q", "R", "S", "T"], ["U", "V", "W", "X", "Y", "C"],
# ["Z", ",", ".", " ", "?"]]:each_with_index>:with_object([])>
(Since enum0 is an instance of the class Enumerator, each_object must be a method of that class.)
As you see, enum1 is another enumerator, which you might think of as a "compound" enumerator. (Inspect the return value above carefully.) We can view the elements of this enumerator by converting it to an array:
enum1.to_a
#=> [[[["A", "B", "C", "D", "E"], 0], []],
# [[["F", "C", "G", "H", "I", "J"], 1], []],
# [[["K", "L", "M", "N", "O"], 2], []],
# [[["P", "Q", "R", "S", "T"], 3], []],
# [[["U", "V", "W", "X", "Y", "C"], 4], []],
# [[["Z", ",", ".", " ", "?"], 5], []]]
enum1 contains six elements, the first being:
[[["A", "B", "C", "D", "E"], 0], []]
The elements of enum1 are passed into the block by Enumerator#each (which calls Array#each). We can use Enumerator#next to sequentially obtain those elements and set the block variables equal to them:
(a,i),arr = enum1.next
#=> [[["A", "B", "C", "D", "E"], 0], []]
a #=> ["A", "B", "C", "D", "E"]
i #=> 0
arr #=> []
The array passed into the block is broken down by Ruby's use of "parallel assignment" and "disambiguation".
We can now perform the block calculation:
j = a.index(target)
#=> j = ["A", "B", "C", "D", "E"].index("C")
#=> 2
arr << [i,j] if j
#=> [] << [0,2] if 2
#=> [[0, 2]]
So now arr (which will be returned when the calculations are complete) equals [[0, 2]].
The next element of enum1 is now passed into the block and assigned to the block variables:
(a,i),arr = enum1.next
#=> [[["F", "C", "G", "H", "I", "J"], 1], [[0, 2]]]
a #=> ["F", "C", "G", "H", "I", "J"]
i #=> 1
arr #=> [[0, 2]]
Notice that arr has been updated. We now perform the block calculation:
j = a.index(target)
#=> ["F", "C", "G", "H", "I", "J"].index("C")
#=> 1
arr << [i,j] if j
#=> [[0, 2]] << [1,1] if 1
#=> [[0, 2], [1, 1]]
The third element of enum1 is passed to the block:
(a,i),arr = enum1.next
#=> [[["K", "L", "M", "N", "O"], 2], [[0, 2], [1, 1]]]
a #=> ["K", "L", "M", "N", "O"]
i #=> 2
arr #=> [[0, 2], [1, 1]]
j = a.index(target)
#=> ["K", "L", "M", "N", "O"].index("C")
#=> nil
arr << [i,j] if j
#=> [[0, 2], [1, 1]] << [2,nil] if nil
so arr is not altered. The remainIng calculations are similar.

Is there any way to create 2 x 2 array/matrix from a larger array/matrix

New to Ruby and learning at the moment.
I am not sure I should use arrays or matrix for this.
I have arrays
[['J','O','I','J','O'],
['I','J','O','J','O'],
['I','I','J','I','J']]
I want to find out the following as you can see in the image.
['J', 'O']
['I', 'J']
What I thought was using Ruby Matrix, but I am not sure if I can divide the original array/matrix to chunk of small 2 by 2 matrix and if it matches with [J, O], [I, J].
Or should I use array and loop through.
I appreciate any inputs.
I suggest you use the method Matrix#minor to do this.
Code
require 'matrix'
def find_in_matrix(arr,sub)
sub_nrows = sub.size
sub_ncols = sub.first.size
rows = Array[*0..arr.size-sub_nrows]
cols = Array[*0..arr.first.size-sub_ncols]
arr_m = Matrix[*arr]
sub_m = Matrix[*sub]
rows.product(cols).select {|i,j| arr_m.minor(i,sub_nrows,j,sub_ncols)==sub_m}
end
Example
arr = [['J','O','I','J','O'],
['I','J','O','J','O'],
['I','I','J','I','J']]
sub = [['J', 'O'],
['I', 'J']]
find_in_matrix(arr,sub) #=> [[0, 0], [1, 1], [1, 3]]
find_in_matrix(arr, [['O'], ['J']]) #=> [[0, 1], [1, 2], [1, 4]]
find_in_matrix(arr, [['O']]) #=> [[0, 1], [0, 4], [1, 2], [1, 4]]
find_in_matrix(arr, [['I','J','O']]) #=> [[0, 2], [1, 0]]
find_in_matrix(arr, [['I','J'],['J','O']]) #=> []
find_in_matrix(arr, [[]]) #=> [[0, 0], [0, 1],...,[0, 5]]
# [1, 0], [1, 1],...,[1, 5]]
# [2, 0], [2, 1],...,[2, 5]]
Explanation
For the example above:
sub_nrows = sub.size #=> 2
sub_ncols = sub.first.size #=> 2
rows = Array[*0..(arr.size-sub_nrows)] #=> [0, 1]
cols = Array[*0..(arr.first.size-sub_ncols)] #=> [0, 1, 2, 3]
arr_m = Matrix[*arr]
#=> Matrix[["J", "O", "I", "J", "O"], ["I", "J", "O", "J", "O"],
# ["I", "I", "J", "I", "J"]]
sub_m = Matrix[*sub]
#=> Matrix[["J", "O"], ["I", "J"]]
a = rows.product(cols)
#=> [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3]]
a.select {|i,j| arr_m.minor(i,sub_nrows,j,sub_ncols)==sub_m}
#=> [[0, 0], [1, 1], [1, 3]]
Consider the first element of a that select passes into the block: [0, 0] (i.e., the block variables i and j are both assigned the value zero). We therefore compute:
arr_m.minor(i,sub_nrows,j,sub_ncols) #=> arr_m.minor(0,2,0,2)
#=> Matrix[["J", "O"], ["I", "J"]]
As
arr_m.minor(0,2,0,2) == sub_m
[0, 0] is selected. On the other hand, for the element [1, 2] of a, i => 1, j => 2, so:
arr_m.minor(i,sub_nrows,j,sub_ncols) #=> arr_m.minor(1,2,2,2)
#=> Matrix[["O", "J"], ["J", "I"]]
which does not equal sub_m, so the element [1, 2] is not selected.
Note that Matrix#minor has two forms. I used the form that takes four parameters. The other form takes two ranges as parameters.
Not directly to answer the question, and not an efficient way to get the matched position:
matrix = [
%w(J O I J O),
%w(I J O J O),
%w(I I J I J)
]
target = [
%w(J O),
%w(I J)
]
matrix.each_cons(target.length).each_with_index do |sub, row|
sub.map{|a| a.each_cons(target[0].length).to_a}.tap do |sub|
head = sub.shift
head.zip(*sub).each_with_index do |m, col|
if m == target
puts "#{row}, #{col}"
end
end
end
end
You can define the following:
def find_in_matrix(matrix, target)
(0..matrix.length-target.length).to_a.product(
(0..matrix.first.length-target.first.length).to_a).select do |x, y|
(0...target.length).to_a.product(
(0...target.first.length).to_a).all? do |test_x, test_y|
matrix[x+test_x][y+test_y] == target[test_x][test_y]
end
end
end
matrix = [["J", "O", "I", "J", "O"],
["I", "J", "O", "J", "O"],
["I", "I", "J", "I", "J"]]
target = [["J", "O"],
["I", "J"]]
find_in_matrix(matrix, target)
=> [[0, 0], [1, 1], [1, 3]]
This solution simply goes over all the sub-matrices of matrix with target's size, and selects the ones that are equal to it.

Ruby Counting chars in a sequence not using regex

Need help with this code on counting chars in a sequence.
This is what I want:
word("aaabbcbbaaa") == [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
word("aaaaaaaaaa") == [["a", 10]]
word("") == []
Here is my code:
def word(str)
words=str.split("")
count = Hash.new(0)
words.map {|char| count[char] +=1 }
return count
end
I got word("aaabbcbbaaa") => [["a", 6], ["b", 4], ["c", 1]], which is not what I want. I want to count each sequence. I prefer a none regex solution. Thanks.
Split string by chars, then group chunks by char, then count chars in chunks:
def word str
str
.chars
.chunk{ |e| e }
.map{|(e,ar)| [e, ar.length] }
end
p word "aaabbcbbaaa"
p word("aaaaaaaaaa")
p word ""
Result:
[["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
[["a", 10]]
[]
If you don't want to use a regex, you may just have to do something like:
def word(str)
last, n, result = str.chars.first, 0, []
str.chars.each do |char|
if char != last
result << [last, n]
last, n = char, 1
else
n += 1
end
end
result << [last, n]
end
I'd like to use some higher-order function to make this more concise, but there's no appropriate one in the Ruby standard library. Enumerable#partition almost does it, but not quite.
I'd do the following. Note that each_char is a newer method (Ruby 1.9?) that might not be available on your version, so stick with words=str.split("") in that case.
def word(str)
return [] if str.length == 0
seq_count = []
last_char = nil
count = 0
str.each_char do |char|
if last_char == char
count += 1
else
seq_count << [last_char, count] unless last_char.nil?
count = 1
end
last_char = char
end
seq_count << [last_char, count]
end
[52] pry(main)> word("hello")
=> [["h", 1], ["e", 1], ["l", 2], ["o", 1]]
[54] pry(main)> word("aaabbcbbaaa")
=> [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
[57] pry(main)> word("")
=> []
Another non-regexp-version.
x = "aaabbcbbaaa"
def word(str)
str.squeeze.reverse.chars.each_with_object([]) do |char, list|
count = 0
count += 1 until str.chomp!(char).nil?
list << [char, count]
end
end
p word(x) #=> [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
If the world were without regex and chunk:
def word(str)
a = str.chars
b = []
loop do
return b if a.empty?
c = a.slice_before {|e| e != a.first}.first
b << [c.first, c.size]
a = a[c.size..-1]
end
end
word "aaabbcbbaaa" # => [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
word "aaa" # => [["a",3]]
word "" # => []
Here's another way. Initially I tried to find a solution that didn't require conversion of the string to an array of its characters. I couldn't come up with anything decent until I saw #hirolau 's answer, which I modified:
def word(str)
list = []
char = str[-1]
loop do
return list if str.empty?
count = 0
count += 1 until str.chomp!(char).nil?
list.unshift [char, count]
char = str[-1]
end
end
You can use this pattern with scan:
"aaabbcbbaaa".scan(/((.)\2*)/)
and after count the number of char for all group 1
example:
"aaabbcbbaaaa".scan(/((.)\2*)/).map do |x,y| [y, x.length] end

Count consecutives

I need to write a method that does the following
consecutive_count("aaabbcbbaaa") == [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
I got the code, but it looks ugly and I'm trying to see a better solution, please advice.
Here is my code:
def consecutive_count(str)
el = str[0]; count = 0; result = []
str.split("").each do |l|
if (el != l)
result << [el, count]
count = 1
el = l
else
count +=1
end
end
result << [el, count] if !el.nil?
return result
end
Here is one way :
s = "aaabbcbbaaa"
s.chars.chunk{|e| e }.map{|item,ary| [item,ary.size]}
# => [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
"aaabbcbbaaa".scan(/(?<s>(?<c>.)\k<c>*)/).map{|s, c| [c, s.length]}
# => [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
or
"aaabbcbbaaa".scan(/((.)\2*)/).map{|s, c| [c, s.length]}
# => [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
A solution which does not involve regex magic (although those are a bit shorter and probably faster) is this:
str.each_char.each_with_object([]) do |char, result|
if (result.last || [])[0] == char
result.last[1] += 1
else
result << [char, 1]
end
end
Depending on your level of understanding, it might better transport your intended meaning which might help to debug the thing in 6 month :)
Regexp solution:
my_s = "aaabbcbbaaa"
p my_s.scan(/(.)(\1*)/).map{|x,y| [x, y.size + 1]}
#=> [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
or
a, result = "aaabbcbbaaa", []
result << a.slice!(/(\w)\1*/) until a.empty?
and then map the result with counts.
You can try:
def consecutive_count(str)
result = {}
array = str.split(//).uniq
array.each.map {|char| result[char] = 0}
array.each do |char|
while str.starts_with?(char) do
result[char] += 1
str[0] = ""
end
result
end

Ruby String Encode Consecutive Letter Frequency

I want to encode a string in Ruby such that output should be in pairs so that I could decode it. I want to encode in such a way that each pair contains the next distinct letter in the string, and the number consecutive repeats.
e.g If I encode "aaabbcbbaaa" output should
[["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
here is the code.
def encode( s )
b = 0
e = s.length - 1
ret = []
while ( s <= e )
m = s.match( /(\w)\1*/ )
l = m[0][0]
n = m[0].length
ret << [l, n]
end
ret
end
"aaabbcbbaaa".chars.chunk{|i| i}.map{|m,n| [m,n.count(m)]}
#=> [["a", 3], ["b", 2], ["c", 1], ["b", 2], ["a", 3]]
"aaabbcbbaaa".scan(/((.)\2*)/).map{|s, c| [c, s.length]}
You could also do this procedurally.
def group_consecutive(input)
groups = []
input.each_char do |c|
if groups.empty? || groups.last[0] != c
groups << [c, 1]
else
groups.last[1] += 1
end
end
groups
end
'aaabbcbbaaa'.scan(/((.)\2*)/).map {|e| [e[1], e[0].size]}

Resources