How to DRY out this ruby code - ruby

I would like to DRY out this code. I can't seem to figure out how though.
def get_all_verb_nodes
all_verb_nodes = #all_nodes_ins_del_nodes.select { |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
words = get_node_text(node)
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words.length <= 2 && words.any? { |word| is_a_verb?(base_word+word) || is_a_verb?(word) && !is_pluralized?(base_word+word, base_word+next_node_word) } && !next_node_is_insert_or_delete
}
end
def get_all_article_nodes
all_article_nodes = #all_nodes_ins_del_nodes.select { |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
words = get_node_text(node)
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words.length <= 2 && words.any? { |word| #articleset.include?(word) || (#articleset.include?(base_word) if word == 'n') } && !#articleset.include?(next_node_word) && !next_node_is_insert_or_delete
}
end
Both are almost identical except for the last line which defines the specific requirement of the function.
Any ideas appreciated.

Here is a first cut at it. I moved things around so they were grouped logically and to facilitate the yield.
def get_all_nodes
#all_nodes_ins_del_nodes.select do |node|
previous_node = node.xpath('preceding-sibling::w:r').last
base_word = previous_node.text.split.last.strip.delete('.!?,:') if previous_node
next_node = node.next_element
next_node_is_insert_or_delete = is_insert_or_delete?(next_node.name) if next_node
next_node_word = next_node.text.strip if next_node
words = get_node_text(node)
words.length <= 2 &&
!next_node_is_insert_or_delete &&
yield(words, base_word, next_node_word)
end
end
all_verb_nodes = get_all_nodes do |words, base_word, next_node_word|
words.any? do |word|
is_a_verb?(base_word + word) ||
is_a_verb?(word) &&
!is_pluralized?(base_word + word, base_word + next_node_word)
end
end
all_article_nodes = get_all_nodes do |words, base_word, next_node_word|
!#articleset.include?(next_node_word) &&
words.any? do |word|
#articleset.include?(word) || (#articleset.include?(base_word) if word == 'n')
end
end

Related

Ruby For loop through JSON response from API

I have a perfectly functional piece of code that does what I want but it is really heavy and i am sure it could be greatly improved by using a nice For loop somewhere but I'm not sure how to go about it
My code is:
def helper
response = RestClient.get API_RESPONSE
check_X_0 = JSON.parse(response.body)['EXP'][0]['X']
check_Y_0 = JSON.parse(response.body)['EXP'][0]['Y']
check_X_1 = JSON.parse(response.body)['EXP'][1]['X']
check_Y_1 = JSON.parse(response.body)['EXP'][1]['Y']
check_X_2 = JSON.parse(response.body)['EXP'][2]['X']
check_Y_2 = JSON.parse(response.body)['EXP'][2]['Y']
check_X_3 = JSON.parse(response.body)['EXP'][3]['X']
check_Y_3 = JSON.parse(response.body)['EXP'][3]['Y']
check_X_4 = JSON.parse(response.body)['EXP'][4]['X']
check_Y_4 = JSON.parse(response.body)['EXP'][4]['Y']
if check_X_0 == false && check_Y_0 == true
exp_id = JSON.parse(response.body)['EXP'][0]['ABC']
elsif check_X_1 == false && check_Y_1 == true
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
elsif check_X_2 == false && check_Y_2 == true
exp_id = JSON.parse(response.body)['EXP'][2]['ABC']
elsif check_X_3 == false && check_Y_3 == true
exp_id = JSON.parse(response.body)['EXP'][3]['ABC']
elsif check_X_4 == false && check_Y_4 == true
exp_id = JSON.parse(response.body)['EXP'][4]['ABC']
else
puts 'Nothing valid - use default'
exp_id = JSON.parse(response.body)['EXP'][1]['ABC']
end
This is fairly cumbersome so can anyone help me to trim this down?
You should not parse your json 10 times in the first place. Parse it once and use the result.
rb = RestClient.get(API_RESPONSE).body['EXP']
checks = (0..4).map { |i, s| [i, rb[i]['X'], rb[i]['Y']] }
exp_id =
if found = checks.detect { |_i, f, t| !f && t }
rb[found.first]['ABC']
else
puts 'Nothing valid - use default'
rb[1]['ABC']
end

Implementing operator precedence in my calculator interpreter

As part of learning Ruby am trying to implement a basic interpreter which reads input and do basic arithmetic calculations. So far basic arithmetic operations are working but having problem in operator precedence. Which is not handled yet. This is the code. Am at a beginner level. Any mistakes in this code are due to my lack of knowledge. How this code can be modified to handle operator precedence.
Sample output
2+2+2 = 6 #correct
10+10/2 = 10 # incorrect as in irb answer must be 15
Github Repo of this interpreter
=begin
Basic calculator Interpreter
can add, substract, multiply , divide with any number of operands at a time
Drawback : Lacks operator precedence
=end
class Interpreter
attr_accessor :input
def initialize
#input = gets.chomp
end
def intepret
first_operand = []
f = []
operator = '+'
array = Array.new
lc = 0
#input.split.join.split("").each_with_index.map do |i, index|
if i.is_number?
first_operand.push(i)
if index == #input.length-1
array.push(first_operand.join("").to_i)
end
elsif i.is_plus?
f = first_operand
first_operand = nil
first_operand = []
array.push(f.join("").to_i)
array.push("+")
elsif i.is_minus?
f = first_operand
first_operand = nil
first_operand = []
operator = '-'
array.push(f.join("").to_i)
array.push("-")
elsif i.is_multi?
f = first_operand
first_operand = nil
first_operand = []
operator = '*'
array.push(f.join("").to_i)
array.push("*")
elsif i.is_divide?
f = first_operand
first_operand = nil
first_operand = []
operator = '/'
array.push(f.join("").to_i)
array.push("/")
else
puts "Illegal input exiting.."
exit
end
lc = lc+1
end
#apply the appropriate operation on the inputs based on the operand
#puts "=======TOKENS======"
#puts array.inspect
result = 0
array.each_with_index.map do |x, key|
result = x if key == 0
if x == '+'
if key == 0
result = add(result, array[key+1])
else
result = add(result, array [key+1])
end
elsif x == '-'
if key == 0
result = minus(result, array[key+1])
else
result = minus(result, array [key+1])
end
elsif x == '*'
if key == 0
result = multi(result, array[key+1])
else
result = multi(result, array [key+1])
end
elsif x == '/'
begin
if key == 0
result = divide(result, array[key+1])
else
result = divide(result, array [key+1])
end
rescue
puts "Zero Divsion error"
exit
end
end
end
puts "Result is: "+result.to_s
end
def print_token(type, value)
puts type + ' '+ value
end
def add(f,s)
return f.to_i + s.to_i
end
def minus(f,s)
return f.to_i - s.to_i
end
def multi(f,s)
return f.to_i * s.to_i
end
def divide(f,s)
return f.to_i / s.to_i
end
end
# Override the string class, to directly use methods like obj.is_number? rather than is_number?(obj)
class String
def is_number?
true if Float(self) rescue false
end
def is_plus?
true if self == '+' rescue false
end
def is_minus?
true if self == '-' rescue false
end
def is_multi?
true if self == '*' rescue false
end
def is_divide?
true if self == '/' rescue false
end
end
#continue accepting inputs until exit CTRL + D
while true
print 'pck>:'
i_obj = Interpreter.new
i_obj.intepret
end
First, process the input using the Shunting-yard algorithm. This should give a list of tokens in Reverse Polish notation (RPN). Then you can evaluate the RPN expression.

Rubocop:method has too many lines

Hello I am new at ruby programming.
Ran rubocop inspection in my project and it says:
Method has too many lines. [13/10] def refresh_status
Here is my methods:
def refresh_status
lost = false
in_progress = false
won = false
#bets.each do |bet|
lost = true if bet.result == :lost
if bet.result == :canceled
#to_return /= bet.odd
won = true
end
in_progress = true if bet.result == :in_progress
won = true if bet.result == :won
end
def_result_after_refresh(lost, in_progress, won)
end
def def_result_after_refresh(lost, in_progress, won)
if lost
#result = :lost
elsif in_progress
#result = :in_progress
elsif won
#result = :won
end
end
Can't find a way to make that method shorter, maybe you could help?
You can use some the Enumerable methods.
def refresh_status
#to_return /= #bets.select { |bet| bet.result == :canceled }.map(&:odd).reduce(1, :*)
results = #bets.map { |bet| bet.result == :cancelled ? :won : bet.result }.uniq
#result = case
when results.include?(:lost) then :lost
when results.include?(:in_progress ) then :in_progress
when results.include?(:won) then :won
end
end

How do I continue tic-tac-toe game from saved yaml file?

I have the following tic-tac-toe game: (I'm a noob, please disregard the design of the class, the game works, that's all I care about for now.)
#a tic tac toe game
class TicTacToe
require "yaml"
attr_accessor :player1, :player2
#crates playes and a game board to play tic tac toe
def initialize()
#player1 = Player.new("Player One", "x")
#player2 = Player.new("Player Two", "o")
#game_board = Board.new
end
#prints the board
def print_board
#game_board.board.each_with_index do |row, index|
puts "#{row.join(" | ")}"
puts "---------" unless index == 2
end
puts
end
#determines whose move it is
def move
if #turn % 2 == 1
player_one_turn
else
player_two_turn
end
#turn += 1
end
def valid_move?(row, col)
if #game_board.board[row][col] == " "
return true
else
return false
end
end
#player ones turn
def player_one_turn
print_board
puts "#{#player1.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player1.shape
else
puts "There's already a shape at that position."
player_one_turn
end
if win?(#player1.shape)
winner(#player1.name)
#winner = true
end
end
#player two's turn
def player_two_turn
print_board
puts "#{#player2.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player2.shape
else
puts "There's already a shape at that position."
player_two_turn
end
if win?(#player2.shape)
winner(#player2.name)
#winner = true
end
end
def win?(shape)
if (#game_board.board[0][0] == shape) && (#game_board.board[0][1] == shape) && (#game_board.board[0][2] == shape)
return true
elsif (#game_board.board[1][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[1][2] == shape)
return true
elsif (#game_board.board[2][0] == shape) && (#game_board.board[2][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][0] == shape) && (#game_board.board[2][0] == shape)
return true
elsif (#game_board.board[0][1] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][1] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][2] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][0] == shape)
return true
else
return false
end
end
def draw?
if #turn > 9
print_board
puts "The game ended in a draw. :)"
#winner = true
return true
end
false
end
def winner(winner_name)
puts "#{winner_name}, YOU WIN!!!"
end
def play
#turn = 1
#winner = false
until #winner
move unless draw?
save
end
end
#a class that generates an empty board
class Board
attr_accessor :board
def initialize
#board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
end
end
#a class that assigns creates plaers and assigns them a shape "x" or "o"
class Player
attr_accessor :name, :shape
def initialize(name, shape)
#name = name
#shape = shape
end
end
def save
yaml = YAML::dump(self)
File.open("save.txt", "w"){|file| file.write(yaml)}
end
def self.load
file = File.read("save.txt")
YAML::load_file(file)
end
end
game = TicTacToe.new
game.play
I want to start playing the game, quit the program in the middle of the game and then come back and finish it later after I call TicTacToe.load. However, when I do this now, the YAML file is loaded, but program does not resume where it's supposed to.
Can someone tell me if there is a way to do what I'm trying to do?
I regionally thought that doing something like YAML::load(self) would automatically load the save state of the file I was referring to via some kind of magic. However, I have come to learn that that the way that I designed my class and the dependencies in my "play" function would not allow me to load the previous state if my file.
When loading a YAML file, one has to load the file to a variable and then manually assign the object values to values of the class. That way, the current state of the variables are pretty much being manually assigned to the instance variables of the class. For example, I could have done something like this: file = YAML.load("file_name"), then assign variables values like: #board = file.board.
Had I known this before, I would have designed my class with less dependencies so that it would be loadable in a much cleaner and more convenient way.

Display number of bytes in data set

I wrote a program to create histograms of IP addresses, URLs, error codes, and frequency of IP visits, and would like to obtain the size, in bytes, of the data collected for each histogram. I looked around and saw a bit about the bytes method, but can't seem to get it to function.
Any idea on how to do that to this bit of code? I'd like to add the "byte puts" line after displaying the filename in each method.
class CommonLog
def initialize(logfile)
#logfile = logfile
end
def readfile
#readfile = File.readlines(#logfile).map { |line|
line.split()
}
#readfile = #readfile.to_s.split(" ")
end
def ip_histogram
#ip_count = 0
#readfile.each_index { |index|
if (#readfile[index] =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ )
puts #readfile[index]
puts #ip_count += 1
end
}
puts File.basename #logfile
end
def url_histogram
#url_count = 0
#readfile.each_index { |index|
if (#readfile[index] =~ /\/{1}(([a-z]{4,})|(\~{1}))\:{0}\S+/ )
puts #readfile[index]
puts #url_count += 1
end
}
puts File.basename #logfile
end
def requests_per_hour
#time_count2 = 0
#time_count3 = 0
#time_count4 = 0
#time_count5 = 0
#time_count6 = 0
#readfile.each_index { |index|
if (#readfile[index] =~ /\:\d{2}\:/ )
#new = #readfile[index].split(":")
if #new[1] == "02"
#time_count2 += 1
elsif #new[1] == "03"
#time_count3 += 1
elsif #new[1] == "04"
#time_count4 += 1
elsif #new[1] == "05"
#time_count5 += 1
elsif #new[1] == "06"
#time_count6 += 1
end
end
}
puts "#{#time_count2} instances during hour 2"
puts "#{#time_count3} instances during hour 3"
puts "#{#time_count4} instances during hour 4"
puts "#{#time_count5} instances during hour 5"
puts "#{#time_count6} instances during hour 6"
puts File.basename #logfile
end
def sorted_list
#codearray = Array.new
#http_code_count = 0
#count200 = 0
#count304 =0
#count301 = 0
#count403 = 0
#readfile.each_index { |index|
if #readfile[index] =~ /([2][0][0]")|([3][0][4])|([3][0][1])|([4][0][3])/
#codearray << #readfile[index]
#http_code_count += 1
if #readfile[index] == '200'
#count200 += 1
elsif #readfile[index] == "304"
#count304 += 1
elsif #readfile[index] == "301"
#count301 += 1
elseif #readfile[index] == "403"
#count403 += 1
end
end
}
#hash_count = 0
#frequencies = Hash.new(0)
#codearray.each { |word| #frequencies[word] += 1 }
#frequencies = #frequencies.sort_by { |a, b| a}
#frequencies.each { |word, frequency| #hash_count += frequency}
#frequencies.each { |key, value|
puts "Error #{key} : #{(value.to_f/#hash_count.to_f)*100}%"
}
puts File.basename #logfile
end
end
my_file = CommonLog.new("test_log")
my_file.readfile
my_file.ip_histogram
my_file.url_histogram
my_file.requests_per_hour
my_file.sorted_list
Assuming that the number of bytes processed is the entire size of each log file you could do something like this:
class CommonLog
attr_reader :bytes_read
def initialize(logfile)
#logfile = logfile
#bytes_read = File.size(logfile)
end
# ... now simply print "bytes_read" when desired ...

Resources