What is wrong with my "stars and bars" implementation? - ruby

Recently came upon a question about Bars and Stars. I can't remember it in its entirety but I got it wrong and I'm breaking my head trying to figure out exactly where I went wrong.
The question is this one https://www.chegg.com/homework-help/questions-and-answers/given-string-stars-bars-determine-number-stars-two-bars-within-substring-star-represented--q38765107
My code was pretty straightforward.
# First attempt
def total_stars(s, start_indices, end_indices)
indices = start_indices.zip(end_indices)
total_count = []
indices.each do |start_i, end_i|
substring = s.slice(start_i - 1, end_i)
substring.gsub!('|', '||') # Add '||' for scan to work
# Scan for '*' between '|'. Count them (-2 for the two bars). Sum them.
count = substring.scan(/\|\*+\|/).map { |str| str.length - 2 }.reduce(&:+) || 0
total_count.push(count)
end
total_count
end
Can anyone help me identify where I could've gone wrong? I've tried searching for the question on HackerRank and LeetCode and could not find it.
And if anyone can send me hackerrank or Leetcode link that would be great.

The only problem I see with your code is s.slice(start_i - 1, end_i). That is problematic when start_i = 0 and end_i = s.size - 1. For example, "abc".slice(0-1, 2) #=> "2", because -1 and 2 both index the last character of the string.
Note you can replace reduce(&:+) || 0 with reduce(0, &:+), or, better, sum.
I do think substring.gsub!('|', '||') is a bit hokey. You could instead write
'**|*|**|**'.scan(/(?<=\|)\*+(?=\|)/).sum(&:size)
#=> 3
where (?<=\|) is a positive lookbehind, which asserts that the match (one or more '*''s) is preceded by a pipe and (?=\|) is a positive lookahead, which asserts that the match is followed by a pipe.
Here is another way.
def find_em(str, start_idx, end_idx)
start_idx.zip(end_idx).map do |s,e|
next 0 unless str[s..e].include?('|')
str[str.index('|', s)..str.rindex('|', e)].count('*')
end
end
find_em('**|*|**|**', [1, 1, 1, 0, 0], [6, 7, 8, 1, 3])
#=> [1, 3, 3, 0, 0]
See Array#zip, String#[], String#index and String#rindex.

Related

bad value for range (ArgumentError) in ruby

I get the desired output but the interpreter throws an ArgumentError-bad value for range ?
Q)Given an array of integers, for each integer output all integers from 1 to that integer; e.g. if the integer was 5, you would output 1, 2, 3, 4, 5.
Solution:
numbers = [7, 3, 5, 2, 1, 8, 4]
counter = 0
loop do
num = numbers[counter]
(1..num).each do |ele|
puts ele
end
counter += 1
end
I'll let someone else explain why your code is broken, except to say that unless this is a classroom exercise (in which case ask your instructor what the point is), the code is not really idiomatic Ruby. Aside from being more complicated than it needs to be, non-idiomatic Ruby is often harder to debug. One of the following examples will do the right thing when using Ruby 3.0.1, and both are much shorter and less error-prone that manually iterating:
# if you just want to print each item
numbers = [7, 3, 5, 2, 1, 8, 4]
numbers.each { puts _1 }
# if you want to print each slice of length n
numbers.each_index do
next unless _1 > 0
p numbers.slice 0, _1
end
If you need your output in a specific format, that wasn't defined in your original question. Both of these options can be modified to suit whatever output format you're looking for.

Lucas Sequence in Ruby

The Lucas Sequence is a sequence of numbers. The first number of the sequence is 2. The second number of the Lucas Sequence is 1. To generate the next number of the sequence, we add up the previous two numbers. For example, the first six numbers of the sequence are: 2, 1, 3, 4, 7, 11, ...
Write a method lucasSequence that accepts a number representing a length as an arg. The method should return an array containing the Lucas Sequence up to the given length. Solve this recursively.
def lucas_sequence(length)
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
seq = lucas_sequence(length - 1)
next_el = seq[-1] + seq[-2]
seq << next_el
seq
end
p lucas_sequence(0) # => []
p lucas_sequence(1) # => [2]
p lucas_sequence(2) # => [2, 1]
p lucas_sequence(3) # => [2, 1, 3]
p lucas_sequence(6) # => [2, 1, 3, 4, 7, 11]
p lucas_sequence(8) # => [2, 1, 3, 4, 7, 11, 18, 29]
**I'm having a hard time understanding the recursion logic behind this. Can someone explain how the computer is solving this?
Does the computer read the length and then add up from [2,1] until it reaches its length? If so, how does it continuously count down? **
Recursion is the programming equivalent of mathematical induction. Given a series, assume that the problem is solved for the previous member of the series and provide the rule for generating this member.
So, consider just these lines:
def lucas_sequence(length)
seq = lucas_sequence(length - 1) # <1>
next_el = seq[-1] + seq[-2] # <2>
seq << next_el # <3>
seq # <4>
end
That says:
You want to know the lucas sequence of a certain length (length). Fine, first tell me the previous lucas sequence, the sequence that is one unit shorter than this (length-1). (That is the recursion: the lucas_sequence method, itself, calls the lucas_sequence method, but with a reduced length value.)
Add up the last two members of that shorter sequence...
...and append the sum to that shorter sequence...
...and the result is this sequence, the one you asked for.
And that's basically all there is to it! The only problem is that there is no place to start. We assume that for the seq of length 4, we have solved 3 already, which we get by assuming that we have solved 2 already, which we get by assuming we have solve 1 already... But we haven't actually solved any of those!
So we begin by backstopping the most degenerate cases:
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
Now the problem is solved if length is 0, 1, or 2, because we just give those answers directly. Okay, so if length is 3, we solve with reference to 2, which is known. Okay, if length is 4, we solve with reference to 3, and I just told you how to do that. Okay, if length is 5, we solve with reference to 4, and I just told you how to do that. And so on, for any length you care to give me.
So it is essentially a modified Fibonacci sequence. Best way to solve most structured sequences is with an Enumerator e.g.
lucas = Enumerator.new do |y|
a,b = 2,1
loop do
y << a
a, b = b, a + b
end
end
Then
lucas.first(10)
#=> [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
First we create a new Enumerator and then assign a and b to your starting values (2 and 1 respectively).
To generate the sequence we use a loop which will lazily yield the values to the yielder (y).
Here we push in a then we assign a to bs value and bs value to a + b in parallel to avoid overwriting a before the addition of a + b.

Adding the number to the according index it is positioned in

def incrementer(num)
num.map.with_index(1) do |row, index|
if row > 9
row.to_s.reverse.chop.to_i
else
index + row
end
end
end
Hi,
I have a method which adds the number to the according index it is positioned in. However I have two rules:
The index starts at (1)
If the number is a multiple, remove the first integer off the end of it. 12 would become 2 for example.
The problem is I am unsure how to include an 'if' statement inside a block and I believe I am doing it wrong. I know there is a much better way to write this statement but I am finding it hard.
Example:
incrementer([4,6,9,1,3]) => [5,8,2,5,8]
- 9 + 3 (position of 9 in array) = 12
- Only its last digit 2 should be returned
Fixing your code
Use map followed by with_index(1), the argument offsetting the initial index by +1.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.digits.first
else
row
end
end
end
incrementer [3, 2, 4, 10] #=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3] #=> [5, 8, 2, 5, 8]
Negative numbers
Numbers can be negative and for which we can use abs.digits but it may be better to use Integer#remainder.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
case row
when -9..9
row
else
row.remainder 10
end
end
end
incrementer [3, 2, 4, 10]
#=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3]
#=> [5, 8, 2, 5,8]
incrementer [3, 2, 4,-17]
#=> [4, 4, 7, -3]
incrementer [4, 6, -22, 1, 3]
#=> [5, 8, -9, 5, 8]
Why not use % instead of Integer#remainder?
a % b returns a modulo b which is not the same thing as the remainder. This has consequences for negative numbers:
-12 % 10 #=> 8 not the last digit
So we need to use Integer#remainder, so:
-12.remainder 10 #=> -2
Which is what we need, i.e. the last digit (parity included). It can be noted in other languages such as JavaScript, a % b returns the remainder.
As noted by #moveson the above code can be refactored to:
num.map.with_index(1) { |row, index| (index + row).remainder 10 }
The #digits method requires Rails or ActiveSupport (or Ruby 2.4+), and it's not necessary. Here is a pure Ruby solution that works with any Ruby version:
def incrementer(array)
array.map.with_index(1) { |integer, index| (integer + index) % 10 }
end
This code above says: For each element of the array, add its index (starting from 1), divide the sum by 10, and return the remainder.
The % (modulo) operator divides the number before it by the number after it and returns the remainder. For example, 22 % 7 returns 1. It's an extremely useful tool and can often help avoid the use of conditionals entirely, such as in your example. Using %, you can get the last digit of a number n (whether or not n is greater than 9) by simply taking n % 10.
Now you can do:
>> incrementer([3,2,4,10])
=> [4, 4, 7, 4]
>> incrementer([4,6,9,1,3])
=> [5, 8, 2, 5, 8]
You've got two separate problems. The first problem is your use of each_with_index. Ruby functions return the value of the last expression they execute, so if you look at your function:
def incrementer(num)
num.each_with_index do |row, index|
# do some stuff
end
end
It essentially calls each_with_index and returns the value. The issue here is that each_with_index iterates over an array and then returns the original array. What you want to do is change each item in the array and return a new array with the new values. For this, you can use map:
def incrementer(num)
num.map.with_index(1) do |row, index|
# do some stuff
end
end
In this case, you can even conveniently pass in the parameter 1 to tell the iterator to start at index 1.
Now the second problem is that your if-else-statement either iterates a number or wraps it around. But what you actually want to do is iterate a number and wrap it around if it's bigger than 9. You can do that like so:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.to_s.reverse.chop.to_i
else
row
end
end
end
What we're doing here is iterating the number first, saving it into row and then checking to see if it's over 9. If it is, we return the last digit; otherwise, we just return the value row.
This code will do what you want, but let's make one more change for the sake of clarity. This code:
row.to_s.reverse.chop.to_i
Is a little bit confusing. Ruby 2.4 has a convenient method for getting at the digits of an integer:
row.digits.first
This is easy to read, but it's a bit slow since it turns an integer into an array of integers. A better way is to use modulus % to get the remainder of your number, divided by 10. This is fast and easy to read for most programmers, since it's a common trick.
row % 10
Putting it all together, you get:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row % 10
else
row
end
end
end
Special thanks to (#sagarpandya84) and (#moveson) for allowing me to build on their answers.

How do I iterate through the digits of an integer? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Turning long fixed number to array Ruby
Well, I have to iterate over the digits of a integer in Ruby. Right now I was just splitting it up into an array, and then iterating over that. However I was wondering if there was a faster way to do this?
The shortest solution probably is:
1234.to_s.chars.map(&:to_i)
#=> [1, 2, 3, 4]
A more orthodox mathematical approach:
class Integer
def digits(base: 10)
quotient, remainder = divmod(base)
quotient == 0 ? [remainder] : [*quotient.digits(base: base), remainder]
end
end
0.digits #=> [0]
1234.digits #=> [1, 2, 3, 4]
0x3f.digits(base: 16) #=> [3, 15]
You can use the old trick of modulus/divide by 10, but this won't be measurably faster unless you have huge numbers, and it will give the digits to you backwards:
i = 12345
while i > 0
digit = i % 10
i /= 10
puts digit
end
Output:
5
4
3
2
1
split=->(x, y=[]) {x < 10 ? y.unshift(x) : split.(x/10, y.unshift(x%10))}
split.(1000) #=> [1,0,0,0]
split.(1234) #=> [1,2,3,4]
Ruby has divmod, which will calculate both x%10and x/10 in one go:
class Integer
def split_digits
return [0] if zero?
res = []
quotient = self.abs #take care of negative integers
until quotient.zero? do
quotient, modulus = quotient.divmod(10) #one go!
res.unshift(modulus) #put the new value on the first place, shifting all other values
end
res # done
end
end
p 135.split_digits #=>[1, 3, 5]
For things like Project Euler, where speed is of some importance, this is nice to have. Defining it on Integer causes it to be available on Bignum too.
I like to use enumerators for this purpose:
class Integer
def digits
to_s.each_char.lazy.map(&:to_i)
end
end
This gives you access to all the good Enumerator stuff:
num = 1234567890
# use each to iterate over the digits
num.digits.each do |digit|
p digit
end
# make them into an array
p num.digits.to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# or take only some digits
p num.digits.take(5) # => [1, 2, 3, 4, 5]
# ...
Try mod by 10 (will give you the last digit), then divide by 10 (will give you the rest of digits), repeat this until you're down to the final digit. Of course, you'll have to reverse the order if you want to go through the digits from left to right.

Chunk a Ruby array according to streaks within it

Summary: The basic question here was, I've discovered, whether you can pass a code block to a Ruby array which will actually reduce the contents of that array down to another array, not to a single value (the way inject does). The short answer is "no".
I'm accepting the answer that says this. Thanks to Squeegy for a great looping strategy to get streaks out of an array.
The Challenge: To reduce an array's elements without looping through it explicitly.
The Input: All integers from -10 to 10 (except 0) ordered randomly.
The Desired Output: An array representing streaks of positive or negative numbers. For instance, a -3 represents three consecutive negative numbers. A 2 represents two consecutive positive numbers.
Sample script:
original_array = (-10..10).to_a.sort{rand(3)-1}
original_array.reject!{|i| i == 0} # remove zero
streaks = (-1..1).to_a # this is a placeholder.
# The streaks array will contain the output.
# Your code goes here, hopefully without looping through the array
puts "Original Array:"
puts original_array.join(",")
puts "Streaks:"
puts streaks.join(",")
puts "Streaks Sum:"
puts streaks.inject{|sum,n| sum + n}
Sample outputs:
Original Array:
3,-4,-6,1,-10,-5,7,-8,9,-3,-7,8,10,4,2,5,-2,6,-1,-9
Streaks:
1,-2,1,-2,1,-1,1,-2,5,-1,1,-2
Streaks Sum:
0
Original Array:
-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10
Streaks:
-10,10
Streaks Sum:
0
Note a few things:
The streaks array has alternating positive and negative values.
The sum of the elements streaks array is always 0 (as is the sum of the original).
The sum of the absolute values of the streak array is always 20.
Hope that's clear!
Edit: I do realize that such constructs as reject! are actually looping through the array in the background. I'm not excluding looping because I'm a mean person. Just looking to learn about the language. If explicit iteration is necessary, that's fine.
Well, here's a one-line version, if that pleases you more:
streaks = original_array.inject([]) {|a,x| (a.empty? || x * a[-1] < 0 ? a << 0 : a)[-1] += x <=> 0; a}
And if even inject is too loopy for you, here's a really silly way:
streaks = eval "[#{original_array.join(",").gsub(/((\-\d+,?)+|(\d+,?)+)/) {($1[0..0] == "-" ? "-" : "") + $1.split(/,/).size.to_s + ","}}]"
But I think it's pretty clear that you're better off with something much more straightforward:
streaks = []
original_array.each do |x|
xsign = (x <=> 0)
if streaks.empty? || x * streaks[-1] < 0
streaks << xsign
else
streaks[-1] += xsign
end
end
In addition to being much easier to understand and maintain, the "loop" version runs in about two-thirds the time of the inject version, and about a sixth of the time of the eval/regexp one.
PS: Here's one more potentially interesting version:
a = [[]]
original_array.each do |x|
a << [] if x * (a[-1][-1] || 0) < 0
a[-1] << x
end
streaks = a.map {|aa| (aa.first <=> 0) * aa.size}
This uses two passes, first building an array of streak arrays, then converting the array of arrays to an array of signed sizes. In Ruby 1.8.5, this is actually slightly faster than the inject version above (though in Ruby 1.9 it's a little slower), but the boring loop is still the fastest.
new_array = original_array.dup
<Squeegy's answer, using new_array>
Ta da! No looping through the original array. Although inside dup it's a MEMCPY, which I suppose might be considered a loop at the assembler level?
http://www.ruby-doc.org/doxygen/1.8.4/array_8c-source.html
EDIT: ;)
original_array.each do |num|
if streaks.size == 0
streaks << num
else
if !((streaks[-1] > 0) ^ (num > 0))
streaks[-1] += 1
else
streaks << (num > 0 ? 1 : -1)
end
end
end
The magic here is the ^ xor operator.
true ^ false #=> true
true ^ true #=> false
false ^ false #=> false
So if the last number in the array is on the same side of zero as the number being processed, then add it to the streak, otherwise add it to the streaks array to start a new streak. Note that sine true ^ true returns false we have to negate the whole expression.
Since Ruby 1.9 there's a much simpler way to solve this problem:
original_array.chunk{|x| x <=> 0 }.map{|a,b| a * b.size }
Enumerable.chunk will group all consecutive elements of an array together by the output of a block:
>> original_array.chunk{|x| x <=> 0 }
=> [[1, [3]], [-1, [-4, -6]], [1, [1]], [-1, [-10, -5]], [1, [7]], [-1, [-8]], [1, [9]], [-1, [-3, -7]], [1, [8, 10, 4, 2, 5]], [-1, [-2]], [1, [6]], [-1, [-1, -9]]]
This is almost exactly what OP asks for, except the resulting groups need to be counted up to get the final streaks array.
More string abuse, a la Glenn McDonald, only different:
runs = original_array.map do |e|
if e < 0
'-'
else
'+'
end
end.join.scan(/-+|\++/).map do |t|
"#{t[0..0]}#{t.length}".to_i
end
p original_array
p runs
# => [2, 6, -4, 9, -8, -3, 1, 10, 5, -7, -1, 8, 7, -2, 4, 3, -5, -9, -10, -6]
# => [2, -1, 1, -2, 3, -2, 2, -1, 2, -4]

Resources