Ruby - Instance variable value changing by itself - arrays

An instance variable in my code keeps on having its value reassigned despite no commands for it to do so. Essentially, the variable is only called in twice: once, to assign its value at startup and then to copy its value to another variable. The code I am working on is a bit complex for me to post it here completely, but this is a basic rundown of it:
class Test
def self.initialize
#problem_var = [ ["1 2 3", "4 5 6"], ["a b c", "d e f"], ["bar", "foo"] ]
end
def self.main_method(parVar)
data = #problem_var
result = "Lorem"
#Iterate through subarrays
data.each do |dataBlock|
#Some code here
if condition then
#The first subarray meets the condition
char = dataBlock[1]
#At this point char is equal to "4 5 6"
##problem_var still holds its original value of:
# [ ["1 2 3", "4 5 6"], ["a b c", "d e f"], ["bar", "foo"] ]
result = OtherModule.replace_six(char)
#By this point, char is now equal to "4 5 7"
#Curiously #problem_var is now equal to:
# [ ["1 2 3, "4 5 7"], ["a b c", "d e f"], ["bar", "foo"] ]
end
end
#More code here
return result
end
end
Something weird happens to the variable right after result has a value assigned to it. Furthermore, this only seems to happen once so that if the code were to run again and alter that 7 to say... an 8, #problem_var would not be updated. Changing #problem_var to a constant has not been able to prevent it from being altered. I have mulled over this for the past two weeks and haven't been able to figure it out. Anyone know what could be happening?
Edit:
You guys were right! The issue was in OtherModule. I was using gsub! on the parameter variable that received char. Here's the simplified OtherModule code for future reference:
module OtherModule
def replace_six(input)
modified_string = ""
if condition(input) then
#Input meets condition
first_string = replace_numbers(input)
#The following method doesn't really apply here
second_string = replace_letters(first_string)
modified_string = second_string
end
return modified_string
end
def replace_numbers(text)
#Some code here
#The following condition for numbers in `text`
if condition(text) then
text.gsub!("6", numberFunction)
#numberFunction returns a string
end
return text
end
end

Problem is most likely in OtherModule.replace_six.
If it uses String#replace method, then, String is mutated and its effect will be visible everywhere a reference to its has been kept.
If you do not have access to code of OtherModule, then, do this:
result = OtherModule.replace_six(char.dup)
If you have access to code of OtherModule, then, change the implementation of replace_six such that it uses either String#sub or String#gsub, as they return a copy of modified string instead of mutating the original string.

Related

String incrementer codewars (Ruby) problem

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?

How to understand Ruby's .each and .map

I am having trouble understanding the differences between map and each, and where and when to use them.
I read "What does map do?" and "Ruby Iterators" but wanted some clarification.
If I have:
z = [1,2,3].map {|x| x + 1}
map takes each element in the array z and adds one to each element, however it does not mutate the original array unless I add !.
On the other hand:
y = [1,2,3].each {|x| x + 1}
returns [1,2,3]. This is confusing to me since:
names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }
returns:
Danil is a programmer
Edmund is a programmer
What is exactly going on in my second example that isn't allowing each array element to be increased by 1, while in the last example a string is being attached to everything in the array?
All credits go to Speransky Danil, whom I took these examples off of.
The map method takes an enum given some block, and iterates through it doing some logic. In your case the logic is x+1. As you say it will not mutate anything unless you use !.
each is simply returning the array that is being called.
Let's take an example of:
names = ["bob"]
If we do:
names.each{|names| names + "somestring"}
the output is still ["bob"]. The reason your second example is different is due to the puts.
As an exercise try doing:
y = [1,2,3].each {|x| puts x + 1}
You will get:
2
3
4
[1,2,3]
tl;dr: I use map if I want to change my collection, apply a transformation on it, end up with something different. I use each if I just need to visit every element in a collection.
Key point is: you should use map if you want to apply a transformation on an array (an enumerable in reality, but let's keep it simple at the beginning). Otherwise, if you don't need to change your array, you can simply use each.
Note that in the code below you are not mutating the array but you are simply take advantage of the local string to print each string with a suffix.
names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }
Obviously, you could do the same with map but in this case you don't need it and you have to use an each too to print every element. The code would be
names = ['danil', 'edmund']
names.map! { |name| name + ' is a programmer' }
# or names = names.map { |name| name + ' is a programmer' }
name.each { |name| puts name }
This is covered in Ruby's documentation in multiple places but the easiest to understand for your use is in the Array documentation for each:
each { |item| block } → ary
each → Enumerator
Calls the given block once for each element in self, passing that element as a parameter. Returns the array itself.
If no block is given, an Enumerator is returned.
a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }
produces:
a -- b -- c --
Note that it says "Returns the array itself."
Compare that to map:
map { |item| block } → new_ary
map → Enumerator
Invokes the given block once for each element of self.
Creates a new array containing the values returned by the block.
See also Enumerable#collect.
If no block is given, an Enumerator is returned instead.
a = [ "a", "b", "c", "d" ]
a.collect { |x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
a.map.with_index { |x, i| x * i } #=> ["", "b", "cc", "ddd"]
a #=> ["a", "b", "c", "d"]
Note that it says "Creates a new array containing the values returned by the block."
This example should help knowing the above:
foo = [1,2,3]
foo.each { |i| puts i + 1 } # => [1, 2, 3]
foo.map { |i| i + 1 } # => [2, 3, 4]
# >> 2
# >> 3
# >> 4
where # => is the return value of the block and # >> is the captured STDOUT from puts.
And, knowing all that, use each when you want to display elements in the array or extract and reuse those elements as parameters or to build things. Use map when you want to change the elements of the array into something else.
The difference is that each is performing an action on each element in the array, returning the original array. The action performed possibly mutated the element.
Whereas map is performing an action on each element in the array and returning its result as an array.
In the first case, map:
z = [1,2,3].map {|x| x + 1}
will take each element in the given array and perform the operation in the block and return a new array, so here it returns [2,3,4].
.each executes the block for each of the elements in the array, and it will not change anything in the array, so here it performs x + 1, but it doesn't store it anywhere, hence in the second case it just returns the array.
Now in the third example you posted, you are printing output in the block itself. Again, there is no change in the array itself.

Delete item from array without returning it

I'm trying to write a method that will cause a rspec test like this to pass:
it "starts the thing and move on" do
class.method_1("Name One")
class.method_1("Name Two")
expect(class.method_2).to eq "Some string Name One"
expect(class.method_3).to eq ["Name Two"]
end
method_1 just adds a name to an array, and method_3 returns the array (defined in initialize method):
def method_1(name)
#array << name
end
def method_3
#array
end
I figured it would be pretty simple to interpolate #array[0] into the string and use #array.delete_at(0) to modify the array. Like so:
def method_2
p "Some string #{#array[0]}"
#array.delete_at(0)
end
But that method returns "Name One" instead of the string. If I comment out the delete code, the string returns properly but my array hasn't been modified. I've been in Ruby docs for a long time but #shift has the same issue about returning the removed item.
I'm almost certain I've over complicated this -- what am I missing?
You can collapse all this down to more conventional Ruby like this:
class MyTestClass
attr_reader :array
def initialize
#array = [ ]
end
def push(s)
#array << s
end
def special_shift
"Some string #{#array.shift}"
end
end
Then in terms of usage:
it "starts the thing and move on" do
my_thing.push("Name One")
my_thing.push("Name Two")
expect(my_thing.special_shift).to eq "Some string Name One"
expect(my_thing.array).to eq ["Name Two"]
end
Using names like push and shift which are consistent with Ruby conventions make the purpose and action of a method a lot easier to understand.
When it comes to your implementation of method_3 you forget that you can inline whatever you want inside a #{...} block, even methods that modify things. The p method is used for display, it won't return anything. To return something you need to have it either as the last thing evaluated (implicit) or by using return (explicit).
Change method_2 to the following to get the array back
def method_2
p "Some string #{#array[0]}"
#array.delete_at(0)
#array
end
From array#delete_if on ruby-doc.org
Deletes the element at the specified index, returning that element, or nil if the index is out of range.
Alternatively use object#tapwhich returns self
#array = [1,2,3,4]
#=> [1, 2, 3, 4]
#array.tap {|arr| arr.delete_at(0)}
#=> [2, 3, 4]

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.)

Why does my array read from file contain bytes

I'm writing a little system that parses lines of data in a txt separated by commas,
so to be basic about it I read the file lines into an array, then use .each on the array and split everything by "'" then push it into the holding array which is returned as the database made from the file, I have made two, the first works fine but its data is stored line by line with a keyword, this one works fine, access and return all good.
I'm using a file containing text data like this
476,TACKLE,40,25,30,0,0,1,A3F,move description string with, punctuation and t's
477,ANOTHERATTACK,BLAHBLAHBLAH,1,2,3,4
This would be data parsing kind of right, well
so I go:
$fs = File_SYstem.new
#path = Dir.getwd.to_S + "/desktop/file.txt"
#data_lines = $fs.file_read_lines(#path)
#data = []
#data_lines.each do |line|
#data >> line.split(',')
end
return #data
#this would make an array of the lines, each line being an array of its elements, right?
#data = The_Code_Above_In_A_Class.new(#path)
=>#data
#data[0]
=>"354,FISSURE,10,40,50,blah blah blah, the second half of the text."
#hmmmm
#data[0][0]
=>"354"
So it seems to work fine, but some times, the numbers at the beginning come back as bytes :O
And for example:
#data.each do |line|
puts line[1].to_S #return second element which is name of move
end
This would print a list of the expected names, fine and dandy, but then I get the remaining data I didn't ask for returned below it in an unrecognizable pattern.
Maybe I can do this?
array = [1,2,3]
array = [array,array,array]
array[2][0] = "Hello!"
array.each do |item|
puts item[2]
end
=>"3"
"3"
"Hello!"
=>:
Seems to me this should work since I'm already using close variations of this style somewhere else with success.
Now this is a sample of the real 580 line file:
1,MEGAHORN,Megahorn,000,120,BUG,Physical,85,10,0,00,0,abef,Cool,"Using its tough and impressive horn, the user rams into the target with no letup."
2,ATTACKORDER,Attack Order,000,90,BUG,Physical,100,15,0,00,0,befh,Smart,The user calls out its underlings to pummel the target. Critical hits land more easily.
3,BUGBUZZ,Bug Buzz,046,90,BUG,Special,100,10,10,00,0,bek,Cute,The user vibrates its wings to generate a damaging sound wave. It may also lower the target's Sp. Def stat.
Now this is the class I use to load it up:
class Move_Data_Extracter
def initialize(path)
load $path.to_s + "/source/string_helper.rb"
#load "/mnt/sdcard/pokemon/system/source/string_helper.rb"
#path = path.to_s
#file_lines = $file_system.file_read_lines(#path.to_s)
$movedata = []
#file_lines.each do |line|
$movedata << line.split(",")
end
end
def get_move_id(move_name)
$movedata.each do |move|
if move[1].upcase.to_s == move_name.upcase.to_s
return move[0].to_i
else
return "Move Doesnt Exist In The System!"
end
end
end
end
This is the feedback I got when I accessing the first item in the returned array(s?):
irb(main):002:0> $movedata[0]
=> ["\xEF\xBB\xBF1", "MEGAHORN", "Megahorn", "000", "120", "BUG", "Physical", "8
5", "10", "0", "00", "0", "abef", "Cool", "\"Using its tough and impressive horn
", " the user rams into the target with no letup.\"\n"]
irb(main):003:0> $movedata[0][0]
=> "\xEF\xBB\xBF1"
irb(main):004:0>
Access worked ok this time but the first element is bytes and that each method I'm trying is going so wrong.
Can anyone figure out whats wrong here?
First thing, that's obviously not the code you're using as things like to_S aren't part of ruby and would instantly fail anyway.
Let's clean up the code a bit:
# $fs = File_SYstem.new # this is just not needed
path = File.expand_path "/desktop/file.txt" # instance variables *only* within explicit objects
data_lines = File.read( path ).split ","
I've no idea what the rest of what you've written really means.
This outputs:
# => ["476", "TACKLE", "40", "25", "30", "0", "0", "1", "A3F", "move description string with", " punctuation and t's\n477", "ANOTHERATTACK", "BLAHBLAHBLAH", "1", "2", "3", "4"]
This bit of code - what is it?
array = [1,2,3]
array = [array,array,array] # pure craziness!
array[2][0] = "Hello!"
array.each do |item|
puts item[2]
end
=>"3"
"3"
"Hello!"
=>:
As to why you're getting back bytes, it's because the file is (likely) encoded as UTF-8. Try File.read( path, "r:UTF-8") to get Ruby to use the correct encoding.

Resources