String incrementer codewars (Ruby) problem - arrays

I am working on a string incrementer project on codewars.
Basicly,
Writing a function which increments a string, to create a new string.
If the string already ends with a number, the number should be incremented by 1.
If the string does not end with a number. the number 1 should be appended to the new string.
If the number has leading zeros the amount of digits should be considered.
foo -> foo1
foo001 ->foo002
foobar23 -> foobar24
foo099 -> foo100
My code is : input.gsub(/\d/,"")+input.split().map {|x| x[/\d+/].next!}.join(" ")
https://repl.it/#tanilserbes/ViolentNoteworthyDowngrade . It works on this playground
However it doesnt work on codewars. I get this error:
main.rb:5:in block in increment_string': undefined methodnext!' for nil:NilClass (NoMethodError)`
Any idea?
thanks in advance!.

To see what's going on here, it's beneficial to run each of your commands individual and see what the output is to track down the error:
input = "foo"
input.gsub(/\d/, "") # => "foo"
So the left hand side of the + operator is going to become "foo" and now we need to see what the right hand side of the is:
input.split # => ["foo"]
["foo"].map { |x| x[/\d+/].next! }
As seen in the question, this is where the error happens, so let's dig into the code inside the map block, where the error is:
["foo"].map { |x| p x }
# Outputs: "foo"
So, x == "foo" at this point:
["foo"].map { |x| p x[/\d+/] }
# Outputs: nil
Since the string "foo" doesn't have any digits in it, the regex pulling out the digits from it, to increment them returns nil and then, without any safeguarding you increment that. NilClass doesn't have a method next!, so you get your error.
If the string was instead "foo1", though, you'd get:
["foo1"].map { |x| p x[/\d+/] }
# Outputs: "1"
Which returns the matched string, and then allows you to call next! (which is a synonym of the String#succ! method called out in the comments) on it. The reason it works in the playground is because the string has digits in it, and doesn't take into account or test the case where strings don't (the first example in the text of the question where "foo" should become "foo1").

Here are some points to consider in developing your solution.
If your string were:
str = "ca9t00456"
the desired return value would be:
"ca9t00457"
(Note that the OP's solution would return an incorrect result ("cat10") for this string. The Codewars question does not say that the only digits in the string are those at the end; it only mentions the "number" at the end of the string".)
A reasonable first step would be to divide the string into two parts:
n = str.index(/\d+\z/)
#=> 4
prefix = str[0, n]
#=> "ca9t"
suffix = str[n..-1]
#=> "00456"
See String#index. The regular expression, /\d+\z/, reads, "match one or more (+) digits (\d) followed by the end of the string (\z). The digit '9' is skipped over because it is neither followed by a digit nor is at the end of the string. See also See String#[].
The string we return will begin with (the value held by) prefix, so we can set that aside for now and concentrate on modifying suffix.
One approach would be:
((suffix.to_i) + 1).to_s
#=> "457"
but then we would have to add the correct number of leading zeroes. Here that would be the same as the number of leading zeroes in suffix (2), but if suffix were, for example, 00999, it would be only one (01000). That could be done, but it's messy.
An easier way would be to use the method String#succ, as #steenslag suggested in the comments.
new_suffix = suffix.succ
#=> "00457"
"00999".succ
#=> "01000"
Now we need only combine prefix and new_suffix.
Note what happens if we execute succ on the entire string:
"ca9t0456".succ
#=> "ca9t0457" correct
"ca9t0999".succ
#=> "ca9t1000" correct
"ca9t9999".succ
#=> "ca9u0000" incorrect
As you see, there's a problem with the third example. That's why I chose to divide the string into two parts as a first step.
You need to investigate three other cases. The first is when the prefix is an empty string:
str = "00456"
the second is when the suffix is an empty string:
str = "ca9t"
and the third is when the string is empty:
str = ""
You can check if the previous calculations still work in the first case.
In the second case we would find:
n = str.index(/\d+\z/)
#=> "cat9t".index(/\d+\z/) => nil
The nil value for n tells us that the desired return value is:
str + "1"
#=> "ca9t" + "1" => "ca9t1"
Would that work?

Related

How do I check if an element occurs more than twice in an array, subject to a string pattern?

I'm using Ruby 2.4. I know how to check if an eleemnt has never occurred more than twice in an array, using
data_arr.count(string) <= 2
but what if my array is
["1/5", "2/6", "3/5", "4/7", "3/8", "3/9"]
how do I check that the first number before the "/" never occurs more than twice when it is found before the "/"? That is, in the above example, "1" never occurs more than twice (only in the element "1/5") but "3" occurrs 3 times before the "/".
2.2.1 :005 > array = ["1/5", "2/6", "3/5", "4/7", "3/8", "3/9"]
=> ["1/5", "2/6", "3/5", "4/7", "3/8", "3/9"]
2.2.1 :006 > array.count{|m| m.match(/3\//) }
=> 3
This works by "passing a block" to the count method. The block is evaluated for each item m and if true, the item is 'counted.'
In this case I'm using a regular expression to select items with '3' before the slash.
re: comments, you can interpolate any number you'd like into the regex/string/whatever as follows:
regex: /#{variable}/
string: "#{string}"
You might begin by using hash::new with a default value of zero to construct a counting hash.
arr = ["1/5", "2/6", "3/5", "4/7", "3/8", "3/9"]
h = arr.each_with_object(Hash.new(0)) { |s,h| h[s[/\d+(?=\/)/]] += 1 }
#> {"1"=>1, "2"=>1, "3"=>3, "4"=>1}
Then write
h.any? { |_,v| v > 2 }
#=> true
The first step I would suggest is to do a string-split on each string of your array. That way, you could check for recurrences among the integers.
For example, for each element in your array, do
array[element].split("") and go from there.
If you want to check if ANY numerator* has occurred more than twice in the array you use this code:
data_arr.each do |element|
numerator = element.match(/(.+)\//)
if data_arr.count {|e| e.match(/^#{numerator}\//)} > 2
# do something if the element matches more than twice
end
end
* numerator is the number before the / in division.

Print elements of ruby array line by line after splitting a single string with "\n" condition

I have a single string, achieved using backticks of the following form:
output = `git log`
then, I have splitted the result where there are "\n" and the result went into an array of the form:
array = output.split("\n")
then, I am just trying to output the result in the screen, however, when I am using
array.each do |a|
puts a
end
I am getting as a result a double line of the form:
result after puts
(empty line)
result after puts etc
when my preferred result is a single line of the form:
result after puts
result afters puts etc
I tried to perform this with print, but I am getting:
result after puts result after puts etc
in a single line.
Can you please help me?
The issue is when you split using \n, if there are two \n characters then an empty "" gets added to the array.
eg: test = ["this","","is","test"]
Now if you do,
test.each do |a|
puts a
end
The o/p will be,
this
// a new line will come here.
is
test
so you should reject the empty values,
test2 = test.reject{ |value| value == ""}
test2.each do |a|
puts a
end
Result is,
this
is
test
In same way,
output = `git log`
array = output.split("\n")
array2 = array.reject{ |value| value == ""}
array2.each do |a|
puts a
end
This will give you the correct result.
Thanks to #AndreyDeineko, we have that:
"The issue is when you split using \n if there are two \n characters then an empty "" gets added to the array. Howcome? a = "1\n2\n3\n4\n"; a.split("\n") #=> ["1", "2", "3", "4"].
Therefore, array.each { |a| a } will work for you"
It did not work 100% for me, but using his answer, I manage to achieve the required result which is:
array.each { |a| a }
puts array

Strange output in Ruby Longest Palindrome substring function

I am trying to develop a function which will return the longest palindrome substring of an entered string. I am right now working on breaking up the string so that each subsection could then be analyzed to see if it is a palindromeThe code is as follows:
def longest_palindrome(s)
place = 0
array = s.chars
output = []
while place < s.length
output << (array[0]..array[place]).to_a
place += 1
end
return output
end
if given string "ababa" I would expect the resulting array to look like this:
[["a"],["a","b"],["a","b","a"],["a","b","a","b"],["a","b","a","b","a"]]
However, when i return the output array this is what is stored inside:
[["a"], ["a", "b"], ["a"], ["a", "b"], ["a"], ["a", "b"]]
What about my function is causing this to happen?
Edit:
Not sure if I should start another topic for this. My code is now as follows:
def longest_palindrome(s)
array = s.chars
start = 0
place = 1
output = []
while start < s.length - 1
while place < s.length
output << array[start..place]
place += 1
end
start += 1
end
return output
end
My logic is that this will start at index 0, then progressively capture one character more of the string until the whole string is complete. Then it will start on index 1 and do the same until it has gotten all possible substrings within the string. However, it only returns:
[["a"],["a","b"],["a","b","a"],["a","b","a","b"],["a","b","a","b","a"]]
Where is the flaw in my logic?
You're misusing the range operator to produce ranges like 'a'..'a', which is just 'a'.
You have two completely independent array indexing operations, each of which return a single element (character) from the array to be used in a range. You're getting array[0], which is always a, and array[place] which alternates between a and b, and producing the ranges 'a'..'a' and 'a'..'b' over and over, which have nothing to do with the arrays the characters originally came from.
You can't build the ranges after extracting the elements from the array and expect the ranges to be produced from the array. The correct sub-array is produced by using the range as the index of the array: array[0..place]. This returns the sub-array from 0 to place, inclusive.

Ruby modify array items and return full array

I have this code here
string.split(/(\w{1,}=)/).each_slice(1).map { |i| items << i }
items.map! do |i|
i = i << str if i.to_s =~ /\w{1,}=/
end
puts items*''
And I want to modify certain items in the array based on regex, then return the full array with the modified items in it. This only returns the modified items. How do I achieve what I'm looking for?
EDIT: Ok, so say I'm trying to split a link using this regex:
page.php?site=blah&id=1
The link is split and added to the array which now contains
page.php?
site=
blah&
id=
1
What I want to do is append some value to the end of the elements ending with a =. This way, when I return the modified array as a string it would output like this:
page.php?site=(newval)&id=(newval)
You have several undefined variables in your example, which is very sloppy.
each_slice(1) is equivalent to each(), so it's not clear why you are using each_slice(1). In any case, both each() and map() step through the items in an Array one by one, but each() returns the original Array unchanged. On the other hand, you use map() when you want to create a new Array that contains changes to the items.
In the regex /\w{1,}/, there is a shortcut for the quantifier {1, }, and it's: +, so most people would write the regex as /\w+/, where + means 1 or more.
I want to modify certain items in the array based on regex, then
return the full array with the modified items in it.
Here is an example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
else
num - 1
end
end
p results
--output:--
[0, 6, 2]
Your current attempt with map() doesn't return anything if the conditional fails. Note how the example above returns something both when the condition fails AND when the condition succeeds. map() replaces an item with whatever is returned for that item.
Now look at this example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
end
end
p results
--output:--
[nil, 6, nil]
If you don't return something for an item, then map() will use nil for that item. In the example, if the condition num == 2 is true then num+4 is returned--but if num == 2 is false, nothing is returned.
Edit:
words = %w[
page.php?
site=
blah&
id=
1
] #=> words = ["page.php?", "site=", "blah&", "id=", "1"]
suffix = 'hello'
results = words.map do |word|
if word.end_with?('=')
"#{word}#{suffix}"
else
word
end
end
p results
--output:--
["page.php?", "site=hello", "blah&", "id=hello", "1"]
Instead of parsing a URL with a regex, have you considered using the addressable gem?
require 'addressable/uri'
uri = Addressable::URI.parse('page.php?site=blah&id=1&bar')
uri.query_values = uri.query_values.map do |k, v|
[k, v.is_a?(String) ? v << 'foo' : v]
end
puts uri.to_s # => page.php?site=blahfoo&id=1foo&bar
This won't handle very complex query parameters (it will just pass them through).
You can use respond_to? :sub! and v.sub! /$/, 'foo' instead of checking types if that makes you uneasy. (I wouldn't use :<< or :concat because those are valid methods for Arrays.)

Adding items to a new array with index

Trying to make a method skip_animals that takes an animals array and a skip integer and returns an array of all elements except first skip number of items.
input: skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
expected output: ["2:fox", "3:wolf"]
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
puts arr.drop(skip)
end
This instead puts each output on a separate line and doesn't add them to the array arr. I thought the arr.push would add them correctly. What do I have to do to get the elements added to the array?
I want to use these methods, not map or something more advanced. I need to tinker with this each_with_index line, not overhaul it.
(This is a challenge on Hackerrank, so it uses STDIN and STDOUT)
EDIT
Here is my updated code with p instead of puts. It's giving me a weird output of two different arrays, not sure why.
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{index}:#{animal}") }
p arr.drop(skip)
end
This gives me two lines of output:
["3:panda", "4:tiger", "5:deer"]
["0:leopard", "1:bear", "2:fox", "3:wolf", "4:dog", "5:cat"]
I'm assuming the top is the correct array, but I don't get why the second is printing also, or why it has a different set of animals.
Use p instead of puts.
irb(main):001:0> puts ['1', '2']
1
2
=> nil
irb(main):002:0> p ['1', '2']
["1", "2"]
According to the documentation, puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line. If called without arguments, outputs a single
record separator.
BTW, I would code like this (using Enumerable#map + returning result instead of printing inside the function):
def skip_animals(animals, skip)
animals.drop(skip).each_with_index.map { |animal, index|
("#{index + skip}:#{animal}")
}
end
p skip_animals(['leopard', 'bear', 'fox', 'wolf'], 2)
just remove puts remove form this line puts arr.drop(skip)
def skip_animals(animals, skip)
arr = Array.new
animals.each_with_index{|animal, index| arr.push("#{animal}:#{index}") }
arr.drop(skip)
end

Resources