I'm trying to work through a level 5 kata by using while loops. Essentially the problem is to turn each letter rotors[n] number of times and then move on to the next rotors number until you get an output word.
flap_display(["CAT"],[1,13,27])
should output ["DOG"]
Here's what I have so far
def flap_display(lines, rotors)
stuff = "ABCDEFGHIJKLMNOPQRSTUVWXYZ?!##&()|<>.:=-+*/0123456789"
i = 0
j = 0
new_word = lines
while i < rotors.length
while j < new_word[0].length
new_word[0][j] = stuff[stuff.index(new_word[0][j]) + rotors[i]]
j += 1
end
i += 1
j = 0
end
new_word
end
This technically traverses the stuff string and assigns the right letters. However it fails two important things: it does not skip each letter when it rotates to the correct position (C should stop rotating when it hits D, A when it hits O etc) and it does not account for reaching the end of the stuff list and eventually returns a nil value for stuff[stuff.index(new_word[0][j]) + rotors[i]]. How can I fix these two problems using basic loops and enumerables or maybe a hash?
A fuller statement of the problem is given here. This is one Ruby-like way it could be done.
FLAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ?!##&()|<>.:=-+*/0123456789"
NBR_FLAPS = FLAPS.size
def flap_display(str, rot)
rot_cum = rot.each_with_object([]) { |n,a| a << a.last.to_i + n }
str.gsub(/./) { |c| FLAPS[(c.ord + rot_cum.shift - 65) % NBR_FLAPS] }
end
flap_display("CAT", [1,13,27])
#=> "DOG"
flap_display("DOG", [-1,-13,-27])
#=> "CAT"
flap_display("CAT", [5,37,24])
#=> "H*&"
'A'.ord #=> 65 and rot_cum contains the cumulative values of rot:
arr = [1, 13, 27]
rot_cum = arr.each_with_object([]) { |n,a| a << a.last.to_i + n }
#=> [1, 14, 41]
I've written a.last.to_i rather than a.last to deal with the case where a is empty, so a.last #=> nil, meaning a.last.to_i => nil.to_i => 0. See NilClass#to_i. Those opposed to such trickery could write:
rot_cum = arr.drop(1).each_with_object([arr.first]) { |n,a| a << a.last + n }
Related
def two_sum(nums, target)
for i in 0..3 - 1
for j in 0..3 - 1
if nums[i] + nums[j] == target && i < j && i != j
puts '[' + (i - 1).to_s + ',' + (j - 1).to_s + ']'
end
end
end
return (i - 1), (j - 1)
end
def main()
nums = Array.new()
target = gets().to_i
nums = gets().to_i
two_sum(nums, target)
end
main()
The requirement of the exercise is to print out numbers whose sum is equal to a target number. You need to get an array of integers and the target number at first.
Can anyone debug it for me? Thank you.
I will leave it others to debug your code. Instead I would like to suggest another way that calculation could be made relatively efficiently.
def two_sum(nums, target)
h = nums.each_with_index.with_object(Hash.new { |h,k| h[k] = [] }) do |(n,i),h|
h[n] << i
end
n,i = nums.each_with_index.find { |n,_i| h.key?(target-n) }
return nil if n.nil?
indices = h[target-n]
return [i,indices.first] unless n == target/2
return nil if indices.size == 1
[i, indices.find { |j| j !=i }]
end
two_sum([2,7,11,15], 9) #=> [0, 1]
two_sum([2,7,11,15], 10) #=> nil
two_sum([2,7,11,15], 4) #=> nil
two_sum([2,7,11,2,15], 4) #=> [0, 3]
two_sum([2,11,7,11,2,15,11], 22) #=> [1, 3]
In the last example
h #=> {2=>[0, 4], 11=>[1, 3, 6], 7=>[2], 15=>[5]}
Note that key lookups in hashes are very fast, specifically, the execution of the line
indices = h[target-n]
Building h has a computational complexity of O(n), where n = num.size and the remainder is very close to O(n) ("very close" because key lookups are close to constant-time), the overall computational complexity is close to O(n), whereas a brute-force approach considering each pair of values in num is O(n^2).
If a hash is defined
h = Hash.new { |h,k| h[k] = [] }
executing h[k] when h has no key k causes
h[k] = []
to be executed. For example, if
h #=> { 2=>[0] }
then
h[11] << 1
causes
h[11] = []
to be executed (since h does not have a key 11), after which
h[11] << 1
is executed, resulting in
h #=> { 2=>[0], 11=>[1] }
By contrast, if then
h[2] << 3
is executed we obtain
h #=> { 2=>[0,3], 11=>[1] }
without h[2] = [] being executed because h already has a key 2. See Hash::new.
Expressing block variables as
|(n,i),h|
is a form of array decomposition.
These were the instructions given on Codewars (https://www.codewars.com/kata/56b5afb4ed1f6d5fb0000991/train/ruby):
The input is a string str of digits. Cut the string into chunks (a chunk here is a substring of the initial string) of size sz (ignore the last chunk if its size is less than sz).
If a chunk represents an integer such as the sum of the cubes of its digits is divisible by 2, reverse that chunk; otherwise rotate it to the left by one position. Put together these modified chunks and return the result as a string.
If
sz is <= 0 or if str is empty return ""
sz is greater (>) than the length of str it is impossible to take a chunk of size sz hence return "".
Examples:
revrot("123456987654", 6) --> "234561876549"
revrot("123456987653", 6) --> "234561356789"
revrot("66443875", 4) --> "44668753"
revrot("66443875", 8) --> "64438756"
revrot("664438769", 8) --> "67834466"
revrot("123456779", 8) --> "23456771"
revrot("", 8) --> ""
revrot("123456779", 0) --> ""
revrot("563000655734469485", 4) --> "0365065073456944"
This was my code (in Ruby):
def revrot(str, sz)
# your code
if sz > str.length || str.empty? || sz <= 0
""
else
arr = []
while str.length >= sz
arr << str.slice!(0,sz)
end
arr.map! do |chunk|
if chunk.to_i.digits.reduce(0) {|s, n| s + n**3} % 2 == 0
chunk.reverse
else
chunk.chars.rotate.join
end
end
arr.join
end
end
It passed 13/14 test and the error I got back was as follows:
STDERR/runner/frameworks/ruby/cw-2.rb:38:in `expect': Expected: "", instead got: "095131824330999134303813797692546166281332005837243199648332767146500044" (Test::Error)
from /runner/frameworks/ruby/cw-2.rb:115:in `assert_equals'
from main.rb:26:in `testing'
from main.rb:84:in `random_tests'
from main.rb:89:in `<main>'
I'm not sure what I did wrong, I have been trying to find what it could be for over an hour. Could you help me?
I will let someone else identify the problem with you code. I merely wish to show how a solution can be speeded up. (I will not include code to deal with edge cases, such as the string being empty.)
You can make use of two observations:
the cube of an integer is odd if and only if the integer is odd; and
the sum of collection of integers is odd if and only if the number of odd integers is odd.
We therefore can write
def sum_of_cube_odd?(str)
str.each_char.count { |c| c.to_i.odd? }.odd?
end
Consider groups of 4 digits in the last example, "563000655734469485".
sum_of_cube_odd? "5630" #=> false (so reverse -> "0365")
sum_of_cube_odd? "0065" #=> true (so rotate -> "0650")
sum_of_cube_odd? "5734" #=> true (so rotate -> "7345")
sum_of_cube_odd? "4694" #=> true (so rotate -> "6944")
so we are to return "0365065073456944".
Let's create another helper.
def rotate_chars_left(str)
str[1..-1] << s[0]
end
rotate_chars_left "0065" #=> "0650"
rotate_chars_left "5734" #=> "7345"
rotate_chars_left "4694" #=> "6944"
We can now write the main method.
def revrot(str, sz)
str.gsub(/.{,#{sz}}/) do |s|
if s.size < sz
''
elsif sum_of_cube_odd?(s)
rotate_chars_left(s)
else
s.reverse
end
end
end
revrot("123456987654", 6) #=> "234561876549"
revrot("123456987653", 6) #=> "234561356789"
revrot("66443875", 4) #=> "44668753"
revrot("66443875", 8) #=> "64438756"
revrot("664438769", 8) #=> "67834466"
revrot("123456779", 8) #=> "23456771"
revrot("563000655734469485", 4) #=> "0365065073456944"
It might be slightly faster to write
require 'set'
ODD_DIGITS = ['1', '3', '5', '7', '9'].to_set
#=> #<Set: {"1", "3", "5", "7", "9"}>
def sum_of_cube_odd?(str)
str.each_char.count { |c| ODD_DIGITS.include?(c) }.odd?
end
The below code is my newbie take on a bubble sort method.
#For each element in the list, look at that element and the element
#directly to it's right. Swap these two elements so they are in
#ascending order.
def bubble_sort (array)
a = 0
b = 1
until (array.each_cons(2).all? { |a, b| (a <=> b) <= 0}) == true do
sort = lambda {array[a] <=> array[b]}
sort_call = sort.call
loop do
case sort_call
when -1 #don't swap
a += 1
b += 1
break
when 0 #don't swap
a += 1
b += 1
break
when 1 #swap
array.insert(a,array.delete_at(b))
a += 1
b += 1
break
else #end of array, return to start
a = 0
b = 1
break
end
end
end
puts array.inspect
end
array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
bubble_sort(array)
I want to be able to alter this method so that it takes a block of code as an argument and uses this to determine how it sorts.
For example:
array = ["hello", "my", "name", "is", "daniel"]
bubble_sort(array) {array[#a].length <=> array[#b].length}
(When I've tried this I've turned a and b into instance variables throughout the code.)
I have tried using yield but I get undefined method 'length' for nil:NilClass once the end of the array is reached. I've tried adding in things such as
if array[#b+1] == nil
#a = 0
#b = 1
end
This helps but I still end up with weird problems like infinite loops or not being able to sort more than certain amount of elements.
Long story short, I have been at this for hours. Is there a simple way to do what I want to do? Thanks.
The way you're calling your lambda is a bit odd. It's actually completely unnecessary. I refactored your code and cleaned up a bit of the redundancy. The following works for me:
def sorted?(arr)
arr.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end
def bubble_sort (arr)
a = 0
b = 1
until sorted?(arr) do
# The yield call here passes `arr[a]` and `arr[b]` to the block.
comparison = if block_given?
yield(arr[a], arr[b])
else
arr[a] <=> arr[b]
end
if [-1, 0, 1].include? comparison
arr.insert(a, arr.delete_at(b)) if comparison == 1
a += 1
b += 1
else
a = 0
b = 1
end
end
arr
end
sample_array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
# Sanity check:
100.times do
# `a` is the value of `arr[a]` in our function above. Likewise for `b` and `arr[b]`.
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
end
EDIT
A cleaner version:
# In place swap will be more efficient as it doesn't need to modify the size of the arra
def swap(arr, idx)
raise IndexError.new("Index #{idx} is out of bounds") if idx >= arr.length || idx < 0
temp = arr[idx]
arr[idx] = arr[idx + 1]
arr[idx + 1] = temp
end
def bubble_sort(arr)
loop do
sorted_elements = 0
arr.each_cons(2).each_with_index do |pair, idx|
comparison = if block_given?
yield pair.first, pair.last
else
pair.first <=> pair.last
end
if comparison > 0
swap(arr, idx)
else
sorted_elements += 1
end
end
return arr if sorted_elements >= arr.length - 1
end
end
# A simple test
sample_array = [4, 2, 2, 2, 2, 2, 5, 5, 6, 3, 23, 5546, 234, 234, 6]
sample_str_array = ["a", "ccc", "ccccc"]
100.times do
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
print bubble_sort(sample_str_array.shuffle) { |a, b| a.length <=> b.length }, "\n"
end
You're not too far off. Just a few things:
Make your function take a block argument
def bubble_sort (array, &block)
Check to see if the user has provided a block
if block_given?
# Call user's comparator block
else
# Use the default behavior
end
Call the user's comparator block
block.call(a, b)
In the user-provided block, accept block params for the elements to compare
bubble_sort(array) {|a,b| a.length <=> b.length}
That should put you in the right ballpark.
I am building a base converter. Here is my code so far:
def num_to_s(num, base)
remainders = [num]
while base <= num
num /= base #divide initial value of num
remainders << num #shovel results into array to map over for remainders
end
return remainders.map{|i| result = i % base}.reverse.to_s #map for remainders and shovel to new array
puts num_to_s(40002, 16)
end
Now it's time to account for bases over 10 where letters replace numbers. The instructions (of the exercise) suggest using a hash. Here is my hash:
conversion = {10 => 'A', 11 => 'B', 12 => 'C', 13 => 'D', 14 => 'E', 15 => 'F',}
The problem is now, how do I incorporate it so that it modifies the array? I have tried:
return remainders.map{|i| result = i % base}.map{|i| [i, i]}.flatten.merge(conversion).reverse.to_s
In an attempt to convert the 'remainders' array into a hash and merge them so the values in 'conversion' override the ones in 'remainders', but I get an 'odd list for Hash' error. After some research it seems to be due to the version of Ruby (1.8.7) I am running, and was unable to update. I also tried converting the array into a hash outside of the return:
Hashes = Hash[remainders.each {|i, i| [i, i]}].merge(conversion)
and I get an 'dynamic constant assignment' error. I have tried a bunch of different ways to do this... Can a hash even be used to modify an array? I was also thinking maybe I could accomplish this by using a conditional statement within an enumerator (each? map?) but haven't been able to make that work. CAN one put a conditional inside an enumerator?
Yes, you could use a hash:
def digit_hash(base)
digit = {}
(0...[10,base].min).each { |i| digit.update({ i=>i.to_s }) }
if base > 10
s = ('A'.ord-1).chr
(10...base).each { |i| digit.update({ i=>s=s.next }) }
end
digit
end
digit_hash(40)
#=> { 0=>"0", 1=>"1", 2=>"2", 3=>"3", 4=>"4",
# 5=>"5", 6=>"6", 7=>"7", 8=>"8", 9=>"9",
# 10=>"A", 11=>"B", 12=>"C", ..., 34=>"Y", 35=>"Z",
# 36=>"AA", 37=>"AB", 38=>"AC", 39=>"AD" }
There is a problem in displaying digits after 'Z'. Suppose, for example, the base were 65. Then one would not know if "ABC" was 10-11-12, 37-12 or 10-64. That's detail we needn't worry about.
For variety, I've done the base conversion from high to low, as one might do with paper and pencil for base 10:
def num_to_s(num, base)
digit = digit_hash(base)
str = ''
fac = base**((0..Float::INFINITY).find { |i| base**i > num } - 1)
until fac.zero?
d = num/fac
str << digit[d]
num -= d*fac
fac /= base
end
str
end
Let's try it:
num_to_s(134562,10) #=> "134562"
num_to_s(134562, 2) #=> "100000110110100010"
num_to_s(134562, 8) #=> "406642"
num_to_s(134562,16) #=> "20DA2"
num_to_s(134562,36) #=> "2VTU"
Let's check the last one:
digit_inv = digit_hash(36).invert
digit_inv["2"] #=> 2
digit_inv["V"] #=> 31
digit_inv["T"] #=> 29
digit_inv["U"] #=> 30
So
36*36*36*digit_inv["2"] + 36*36*digit_inv["V"] +
36*digit_inv["T"] + digit_inv["U"]
#=> 36*36*36*2 + 36*36*31 + 36*29 + 30
#=> 134562
The expression:
(0..Float::INFINITY).find { |i| base**i > num }
computes the smallest integer i such that base**i > num. Suppose, for example,
base = 10
num = 12345
then i is found to equal 5 (10**5 = 100_000). We then raise base to this number less one to get the initial factor:
fac = base**(5-1) #=> 10000
Then the first (base-10) digit is
d = num/fac #=> 1
the remainder is
num -= d*fac #=> 12345 - 1*10000 => 2345
and the factor for the next digit is:
fac /= base #=> 10000/10 => 1000
I made a couple of changes from my initial answer to make it 1.87-friedly (I removed Enumerator#with_object and Integer#times), but I haven't tested with 1.8.7, as I don't have that version installed. Let me know if there are any problems.
Apart from question, you can use Fixnum#to_s(base) to convert base.
255.to_s(16) # 'ff'
I would do a
def get_symbol_in_base(blah)
if blah < 10
return blah
else
return (blah - 10 + 65).chr
end
end
and after that do something like:
remainders << get_symbol_in_base(num)
return remainders.reverse.to_s
I would like to write a program in ruby 1.9.3 ver. which collects unique value ranges and then calculates amount of numbers in these ranges.
For example lets use 3 ranges (1..3), (6..8) and (2..4). It will return array with two ranges (1..4), (6..8) and amount of numbers - 7.
I wrote the following code:
z= []
def value_ranges(start, finish, z)
range = (start..finish)
arr = z
point = nil
if arr.empty?
point = nil
else
arr.each { |uniq|
if overlap?(uniq,range) == true
point = arr.index(uniq)
break
else
point = nil
end
}
end
if point != nil
if arr[point].first >= start && arr[point].end <= finish
range = (start..finish)
elsif arr[point].first >= start
range = (start..arr[point].end)
elsif arr[point].end <= finish
range = (arr[point].first..finish)
else
range = (arr[point].first..arr[point].end)
end
arr[point] = range
else
arr << range
end
print arr
end
def overlap?(x,y)
(x.first - y.end) * (y.first - x.end) >= 0
end
Problem comes when program meets a range which overlaps two already collected ranges.
For example (1..5) (7..9) (11..19) and the next given range is (8..11).
It should link both ranges and return the following result - (1..5),(7..19).
I don't have an idea how to recheck whole array without creating infinite loop. Also what is the best way to calculate amount of numbers in ranges?
Here are two Ruby-like ways of doing it.
arr = [1..3, 6..8, 2..4]
#1 Efficient approach
First calculate the amalgamated ranges:
a = arr[1..-1].sort_by(&:first).each_with_object([arr.first]) do |r,ar|
if r.first <= ar.last.last
ar[-1] = ar.last.first..[ar.last.last,r.last].max
else
ar << r
end
end
#=> [1..4, 6..8]
Then compute the total number of elements in those ranges:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
Explanation
b = arr[1..-1]
#=> [6..8, 2..4]
c = b.sort_by(&:first)
#=> [2..4, 6..8]
enum = c.each_with_object([1..3])
#=> #<Enumerator: [2..4, 6..8]:each_with_object([1..3])>
The contents of the enumerator enum will be passed into the block and assigned to the block variables by Enumerator#each, which will call Array#each. We can see the contents of the enumerator by converting it to an array:
enum.to_a
#=> [[2..4, [1..3]], [6..8, [1..3]]]
and we can use Enumerator#next to step through the enumerator. The first element of the enumerator passed to the block by each is [2..4, [1..3]]. This is assigned to the block variables as follows:
r, ar = enum.next
#=> [2..4, [1..3]]
r #=> 2..4
ar #=> [1..3]
We now perform the block calculation
if r.first <= ar.last.last
#=> 2 <= (1..3).last
#=> 2 <= 3
#=> true
ar[-1] = ar.last.first..[ar.last.last,r.last].max
#=> ar[-1] = 1..[3,4].max
#=> ar[-1] = 1..4
#=> 1..4
else # not executed this time
ar << r
end
This is not so mysterious. So I don't have to keep saying "the last range of ar", let me define:
ar_last = ar.last
#=> 1..3
First of all, because we began by sorting the ranges by the beginning of each range, we know that when each element of enum is passed into the block:
ar_last.first <= r.first
For each element of enum passed into the block for which:
r.first <= ar_last.last
we compare r.last with ar_last.last. There are two possibilities to consider:
r.last <= ar_last.last, in which case the two ranges overlap and therefore ar_last would not change; and
r.last > ar_last.last, in which case the upper end of ar_last must be increased to r.last.
Here,
2 = r.first <= ar_last.last = 3
4 = r.last > ar_last.last = 3
so ar_last is changed from 1..3 to 1..4.
each now passes the last element of enum into the block:
r, ar = enum.next
#=> [6..8, [1..4]]
r #=> 6..8
ar #=> [1..4]
if r.first <= ar.last.last
#=> (6 <= 4) => false this time
...
else # executed this time
ar << r
#=> ar << (6..8)
#=> [1..4, 6..8]
end
and
a = ar #=> [1..4, 6..8]
This time, r.first > ar_last.last, meaning the range r does not overlap ar_last, so we append r to ar, and ar_last now equals r.
Lastly:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
which we could alternatively write:
a.map(&:size).reduce(:+)
#2 Easy but inefficient
Here is an easy, but not especially efficient, method that uses Enumerable#slice_when, newly-minted in v2.2.
arr = [(1..3), (6..8), (2..4)]
To calculate the amagamated ranges:
a = arr.flat_map(&:to_a)
.uniq
.sort
.slice_when { |i,j| i+1 != j }
.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
The total number of elements in those ranges is calculated as in #1
Explanation
Here are the steps:
b = arr.flat_map(&:to_a)
#=> [1, 2, 3, 6, 7, 8, 2, 3, 4]
c = b.uniq
#=> [1, 2, 3, 6, 7, 8, 4]
d = c.sort
#=> [1, 2, 3, 4, 6, 7, 8]
e = d.slice_when { |i,j| i+1 != j }
#=> #<Enumerator: #<Enumerator::Generator:0x007f81629584f0>:each>
a = e.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
We can see the contents of the enumerator e by converting it to an array:
e.to_a
#=> [[1, 2, 3, 4], [6, 7, 8]]