Need help assigning values to a new array then calculating those values - ruby-on-rails

def solve(c)
letter_values =('a'..'z').map.with_index(1) {|letter,value| [letter,value] }
remove_vowels =c.gsub(/[aeiou]/, ' ').split()
end
solve("zodiacs")
The letter_values array has the alphabet letters with numbers. I need to assign those numbers to the remove_vowels variable, which should only return consonants to carry out a calculation later on. If "z" is return, I need to look in the array and take the value of "z" which is 26 and assign it as a value to z from the regex.

So you want the index in the alphabet of each letter?
See this post
letter_values = ('a'..'z').map do |l| l.bytes.first - 96 end

def solve(c)
letter_values =('a'..'z').map.with_index(1) { |letter,value| [letter,value] }.to_h
remove_vowels = c.gsub(/[aeiou]/, '').split('')
hash = Hash.new
remove_vowels.map { | consonants | hash[consonants] = letter_values[consonants]}
hash
end
solve("zodiacs") => this will return {"z"=>26, "d"=>4, "c"=>3, "s"=>19}
Or you could use #each_with_object, the result will be the same; it cleans the code up quite a bit:
def solve(c)
letter_values =('a'..'z').map.with_index(1) { |letter,value| [letter,value] }.to_h
remove_vowels = c.gsub(/[aeiou]/, '').split('')
remove_vowels.each_with_object({}) { |constanant, hash| hash[constanant] = letter_values[constanant]}
end
solve("zodiacs") => this will return {"z"=>26, "d"=>4, "c"=>3, "s"=>19}

Related

How to merge 2 strings alternately in rails?

I have 2 strings:
a = "qwer"
b = "asd"
Result = "qawsedr"
Same is the length of b is greater than a. show alternate the characters.
What is the best way to do this? Should I use loop?
You can get the chars from your a and b string to work with them as arrays and then "merge" them using zip, then join them.
In the case of strings with different length, the array values must be reversed, so:
def merge_alternately(a, b)
a = a.chars
b = b.chars
if a.length >= b.length
a.zip(b)
else
array = b.zip(a)
array.map{|e| e != array[-1] ? e.reverse : e}
end
end
p merge_alternately('abc', 'def').join
# => "adbecf"
p merge_alternately('ab', 'zsd').join
# => "azbsd"
p merge_alternately('qwer', 'asd').join
# => "qawsedr"
Sebastián's answer gets the job done, but it's needlessly complex. Here's an alternative:
def merge_alternately(a, b)
len = [a.size, b.size].max
Array.new(len) {|n| [ a[n], b[n] ] }.join
end
merge_alternately("ab", "zsd")
# => "azbsd"
The first line gets the size of the longer string. The second line uses the block form of the Array constructor; it yields the indexes from 0 to len-1 to the block, resulting in an array like [["a", "z"], ["b", "s"], [nil, "d"]]. join turns it into a string, conveniently calling to_s on each item, which turns nil into "".
Here's another version that does basically the same thing, but skips the intermediate arrays:
def merge_alternately(a, b)
len = [a.size, b.size].max
len.times.reduce("") {|s, i| s + a[i].to_s + b[i].to_s }
end
len.times yields an Enumerator that yields the indexes from 0 to len-1. reduce starts with an empty string s and in each iteration appends the next characters from a and b (or ""—nil.to_s—if a string runs out of characters).
You can see both on repl.it: https://repl.it/I6c8/1
Just for fun, here's a couple more solutions. This one works a lot like Sebastián's solution, but pads the first array of characters with nils if it's shorter than the second:
def merge_alternately(a, b)
a, b = a.chars, b.chars
a[b.size - 1] = nil if a.size < b.size
a.zip(b).join
end
And it wouldn't be a Ruby answer without a little gsub:
def merge_alternately2(a, b)
if a.size < b.size
b.gsub(/./) { a[$`.size].to_s + $& }
else
a.gsub(/./) { $& + b[$`.size].to_s }
end
end
See these two on repl.it: https://repl.it/I6c8/2

How to expand a string in Ruby based on some condition?

I have a string a5bc2cdf3. I want to expand it to aaaaabcbccdfcdfcdf.
In the string is a5, so the resulting string should contain 5 consecutive "a"s, "bc2" results in "bc" appearing 2 times consecutively, and cdf should repeat 3 times.
If input is a5bc2cdf3, and output is aaaaabcbccdfcdfcdf how can I do this in a Ruby method?
def get_character("compressed_string",index)
expanded_string = calculate_expanded_string(compressed_string)
required_char = expanded_string(char_at, index_number(for eg 3))
end
def calculate_expanded_string(compressed_string)
return expanded
end
You may use a regex like
.gsub(/([a-zA-Z]+)(\d+)/){$1*$2.to_i}
See the Ruby online demo
The /([a-zA-Z]+)(\d+)/ will match stubstrings with 1+ letters (([a-zA-Z]+)) and 1+ digits ((\d+)) and will capture them into 2 groups that are later used inside a block to return the string you need.
Note that instead of [a-zA-Z] you might consider using \p{L} that can match any letters.
You want to break out of gsub once the specified index is reached in the original "compressed" string. It is still possible, see this Ruby demo:
s = 'a5bc2cdf3' # input string
index = 5 # break index
result = "" # expanded string
s.gsub!(/([a-zA-Z]+)(\d+)/){ # regex replacement
result << $1*$2.to_i # add to the resulting string
break if Regexp.last_match.end(0) >= index # Break if the current match end index is bigger or equal to index
}
puts result[index] # Show the result
# => b
For brevity, you may replace Regexp.last_match with $~.
I would propose to use scan to move over the compressed string, using a simple RegEx which detects groups of non-decimal characters followed by their count as decimal /([^\d]+)(\d+)/.
def get_character(compressed_string, index)
result = nil
compressed_string.scan(/([^\d]+)(\d+)/).inject(0) do |total_length, (chars, count)|
decoded_string = chars * count.to_i
total_length += decoded_string.length
if index < total_length
result = decoded_string[-(total_length - index)]
break
else
total_length
end
end
result
end
Knowing the current (total) length, one can break out of the loop if the current expanded string includes the requested index. The string is never decoded entirely.
This code gives the following results
get_character("a5bc2cdf3", 5) # => "b"
get_character("a5bc2cdf3", 10) # => "d"
get_character("a5bc2cdf3", 20) # => nil
Just another way. I prefer Wiktor's method by a long way.
def stringy str, index
lets, nums = str.split(/\d+/), str.split(/[a-z]+/)[1..-1].map(&:to_i)
ostr = lets.zip(nums).map { |l,n| l*n }.join
ostr[index]
end
str = 'a5bc2cdf3'
p stringy str, 5 #=> "b"
I'd use:
str = "a5bc2cdf3"
str.split(/(\d+)/).each_slice(2).map { |s, c| s * c.to_i }.join # => "aaaaabcbccdfcdfcdf"
Here's how it breaks down:
str.split(/(\d+)/) # => ["a", "5", "bc", "2", "cdf", "3"]
This works because split will return the value being split on if it's in a regex group: /(\d+)/.
str.split(/(\d+)/).each_slice(2).to_a # => [["a", "5"], ["bc", "2"], ["cdf", "3"]]
The resulting array can be broken into the string to be repeated and its associated count using each_slice(2).
str.split(/(\d+)/).each_slice(2).map { |s, c| s * c.to_i } # => ["aaaaa", "bcbc", "cdfcdfcdf"]
That array of arrays can then be processed in a map that uses String's * to repeat the characters.
And finally join concatenates all the resulting expanded strings back into a single string.

constructing a new hash from the given values

I seem lost trying to achieve the following, I tried all day please help
I HAVE
h = {
"kv1001"=> {
"impressions"=>{"b"=>0.245, "a"=>0.754},
"visitors" =>{"b"=>0.288, "a"=>0.711},
"ctr" =>{"b"=>0.003, "a"=>0.003},
"inScreen"=>{"b"=>3.95, "a"=>5.031}
},
"kv1002"=> {
"impressions"=>{"c"=>0.930, "d"=>0.035, "a"=>0.004, "b"=>0.019,"e"=>0.010},
"visitors"=>{"c"=>0.905, "d"=>0.048, "a"=>0.005, "b"=>0.026, "e"=>0.013},
"ctr"=>{"c"=>0.003, "d"=>0.006, "a"=>0.004, "b"=>0.003, "e"=>0.005},
"inScreen"=>{"c"=>4.731, "d"=>4.691, "a"=>5.533, "b"=>6.025, "e"=>5.546}
}
}
MY GOAL
{
"segment"=>"kv1001=a",
"impressions"=>"0.754",
"visitors"=>"0.711",
"inScreen"=>"5.031",
"ctr"=>"0.003"
}, {
"segment"=>"kv1001=b",
"impressions"=>"0.245",
"visitors"=>"0.288",
"inScreen"=>"3.95",
"ctr"=>"0.003"
}, {
"segment"=>"kv1002=a",
"impressions"=>"0.004"
#... etc
}
My goal is to create a hash with 'kv1001=a' i.e the letters inside the hash and assign the keys like impressions, visitors etc. The example MY GOAL has the format
So format type "kv1001=a" must be constructed from the hash itself, a is the letter inside the hash.
I have solved this now
`data_final = []
h.each do |group,val|
a = Array.new(26){{}}
val.values.each_with_index do |v, i|
keys = val.keys
segment_count = v.keys.length
(0..segment_count-1).each do |n|
a0 = {"segment" => "#{group}=#{v.to_a[n][0]}", keys[i] => v.to_a[n][1]}
a[n].merge! a0
if a[n].count > 4
data_final << a[n]
end
end
end
end`
Here's a simpler version
h.flat_map do |segment, attrs|
letters = attrs.values.flat_map(&:keys).uniq
# create a segment entry for each unique letter
letters.map do |letter|
seg = {"segment" => "#{segment}=#{letter}"}
seg.merge Hash[attrs.keys.map {|key| [key,attrs[key][letter]]}]
end
end
Output:
[{"segment"=>"kv1001=b",
"impressions"=>0.245,
"visitors"=>0.288,
"ctr"=>0.003,
"inScreen"=>3.95},
{"segment"=>"kv1001=a",
"impressions"=>0.754,
"visitors"=>0.711,
"ctr"=>0.003,
"inScreen"=>5.031},
{"segment"=>"kv1002=c",
"impressions"=>0.93,
"visitors"=>0.905,
"ctr"=>0.003,
"inScreen"=>4.731},
{"segment"=>"kv1002=d",
"impressions"=>0.035,
"visitors"=>0.048,
"ctr"=>0.006,
"inScreen"=>4.691},
{"segment"=>"kv1002=a",
"impressions"=>0.004,
"visitors"=>0.005,
"ctr"=>0.004,
"inScreen"=>5.533},
{"segment"=>"kv1002=b",
"impressions"=>0.019,
"visitors"=>0.026,
"ctr"=>0.003,
"inScreen"=>6.025},
{"segment"=>"kv1002=e",
"impressions"=>0.01,
"visitors"=>0.013,
"ctr"=>0.005,
"inScreen"=>5.546}]

Ruby Array conversion best way

What is the best way to achieve the following, I have following array of actions under ABC
ABC:-
ABC:Actions,
ABC:Actions:ADD-DATA,
ABC:Actions:TRANSFER-DATA,
ABC:Actions:EXPORT,
ABC:Actions:PRINT,
ABC:Detail,
ABC:Detail:OVERVIEW,
ABC:Detail:PRODUCT-DETAIL,
ABC:Detail:EVENT-LOG,
ABC:Detail:ORDERS
I want to format this as:
ABC =>{Actions=> [ADD-DATA,TRANSFER-DATA,EXPORT,PRINT], Detail => [Overview, Product-detail, event-log,orders]}
There's probably a ton of ways to do it but here's one:
a = ["ABC:Actions",
"ABC:Actions:ADD-DATA",
"ABC:Actions:TRANSFER-DATA",
"ABC:Actions:EXPORT",
"ABC:Actions:PRINT",
"ABC:Detail",
"ABC:Detail:OVERVIEW",
"ABC:Detail:PRODUCT-DETAIL",
"ABC:Detail:EVENT-LOG",
"ABC:Detail:ORDERS"]
a.map { |action| action.split(":") }.inject({}) do |m, s|
m[s.at(0)] ||= {}
m[s.at(0)][s.at(1)] ||= [] if s.at(1)
m[s.at(0)][s.at(1)] << s.at(2) if s.at(2)
m
end
The map call returns an array where each of the strings in the original array have been split into an array of elements that were separated by :. For example [["ABC","Actions","ADD-DATA"] ... ]
The inject call then builds up a hash by going through each of these "split" arrays. It creates a mapping for the first element, if one doesn't already exist, to an empty hash, e.g. "ABC" => {}. Then it creates a mapping in that hash for the second element, if one doesn't already exist, to an empty array, e.g. "ABC" => { "Detail" => [] }. Then it adds the third element to that array to give something like "ABC" => { "Detail" => ["OVERVIEW"] }. Then it goes onto the next "split" array and adds that to the hash too in the same way.
I will do this as below :
a = ["ABC:Actions",
"ABC:Actions:ADD-DATA",
"ABC:Actions:TRANSFER-DATA",
"ABC:Actions:EXPORT",
"ABC:Actions:PRINT",
"ABC:Detail",
"ABC:Detail:OVERVIEW",
"ABC:Detail:PRODUCT-DETAIL",
"ABC:Detail:EVENT-LOG",
"ABC:Detail:ORDERS"]
m = a.map{|i| i.split(":")[1..-1]}
# => [["Actions"],
# ["Actions", "ADD-DATA"],
# ["Actions", "TRANSFER-DATA"],
# ["Actions", "EXPORT"],
# ["Actions", "PRINT"],
# ["Detail"],
# ["Detail", "OVERVIEW"],
# ["Detail", "PRODUCT-DETAIL"],
# ["Detail", "EVENT-LOG"],
# ["Detail", "ORDERS"]]
m.each_with_object(Hash.new([])){|(i,j),ob| ob[i] = ob[i] + [j] unless j.nil? }
# => {"Actions"=>["ADD-DATA", "TRANSFER-DATA", "EXPORT", "PRINT"],
# "Detail"=>["OVERVIEW", "PRODUCT-DETAIL", "EVENT-LOG", "ORDERS"]}
It was just interesting to do it with group_by :)
a = ['ABC:Actions',
'ABC:Actions:ADD-DATA',
'ABC:Actions:TRANSFER-DATA',
'ABC:Actions:EXPORT',
'ABC:Actions:PRINT',
'ABC:Detail',
'ABC:Detail:OVERVIEW',
'ABC:Detail:PRODUCT-DETAIL',
'ABC:Detail:EVENT-LOG',
'ABC:Detail:ORDERS']
result = a.map { |action| action.split(":") }.group_by(&:shift)
result.each do |k1,v1|
result[k1] = v1.group_by(&:shift)
result[k1].each { |k2,v2| result[k1][k2] = v2.flatten }
end
p result
{"ABC"=>{"Actions"=>["ADD-DATA", "TRANSFER-DATA", "EXPORT", "PRINT"], "Detail"=>["OVERVIEW", "PRODUCT-DETAIL", "EVENT-LOG", "ORDERS"]}}

Return string from multiple array items

I have multiple arrays which have code string items in them. I need to match the code from a given string and then return a class name from the matched array.
Might be better if I show you what I've got. So below are the arrays and underneath this is the string I need to return if the given string matches an item from within the array. So lets say I send a string of '329' this should return 'ss4' as a string:
['392', '227', '179', '176']
= 'ss1'
['389', '386']
= 'ss2'
['371', '338', '335']
= 'ss3'
['368', '350', '332', '329', '323', '185', '182']
= 'ss4'
I need to know what would be the best approach for this. I could create a helper method and have an array for each code block and then check each array to see if the given string code is contained and then return the string, which could be ss1 or ss4. Is this a good idea?
The most efficient approach would be to generate a translator hash once that can perform the lookup super fast:
CODES = {
ss1: ['392', '227', '179', '176'],
ss2: ['389', '386'],
ss3: ['371', '338', '335'],
ss4: ['368', '350', '332', '329', '323', '185', '182']
}
translator = CODES.each_with_object({}){|(s, a), m| a.each{|n| m[n] = s.to_s}}
Now you can simply do:
translator['329']
=> "ss4"
translator['389']
=> "ss2"
def code_to_string(code)
if [395].include? code
"ss1"
elsif [392, 227, 179, 176].include? code
"ss2"
# and so on
end
Note that the codes are integers. to match with a string code, use %w(392 227 179).include? instead of the array
Here's one solution you could try:
CODE_LOOKUP = {
[395] => 'ss1',
[392, 227, 179, 176] => 'ss2',
[389, 386] => 'ss3'
# etc
}
def lookup_code(code)
CODE_LOOKUP.each do |codes_to_test, result|
return result if codes_to_test.include?(code)
end
end
lookup_code(395)
# => "ss1"
lookup_code(179)
# => "ss2"
h = {:ss1 => [395],:ss2 => [392, 227, 179, 176] }
h.key(h.values.find{|x| x.include? "392".to_i})
#=> :ss2
I'd recommend joining all the arrays into a multi-dimensional hash and then searching that.
a1 = ['395']
a2 = ['392', '227', '179', '176']
h = { a1: a1, a2: a2 }
h.select {|a, v| a if v.include?('392') }.keys.first.to_s

Resources