ruby trie implementation reference issue - ruby

I am trying to implement a trie in Ruby but can't figure out what the problem is with my print + collect methods.
I just implemented the same in JS and working fine. I guess the issue could be that Ruby is passed by reference (unlike JS) and how variable assignment works in Ruby.
So if I run the code with string.clone as argument when I recursively call the collect function then I get:
["peter", "peter", "petera", "pdanny", "pdjane", "pdjanck"]
and if I pass string then:
["peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck"]
Any ideas how to fix this?
the code:
class Node
attr_accessor :hash, :end_node, :data
def initialize
#hash = {}
#end_node = false
#data = data
end
def end_node?
end_node
end
end
class Trie
def initialize
#root = Node.new
#words = []
end
def add(input, data, node = #root)
if input.empty?
node.data = data
node.end_node = true
elsif node.hash.keys.include?(input[0])
add(input[1..-1], data, node.hash[input[0]])
else
node.hash[input[0]] = Node.new
add(input[1..-1], data, node.hash[input[0]])
end
end
def print(node = #root)
collect(node, '')
#words
end
private
def collect(node, string)
if node.hash.size > 0
for letter in node.hash.keys
string = string.concat(letter)
collect(node.hash[letter], string.clone)
end
#words << string if node.end_node?
else
string.length > 0 ? #words << string : nil
end
end
end
trie = Trie.new
trie.add('peter', date: '1988-02-26')
trie.add('petra', date: '1977-02-12')
trie.add('danny', date: '1998-04-21')
trie.add('jane', date: '1985-05-08')
trie.add('jack', date: '1994-11-04')
trie.add('pete', date: '1977-12-18')
print trie.print

Ruby's string concat mutates the string and doesn't return a new string. You may want the + operator instead. So basically change the 2 lines inside collect's for-loop as per below:
stringn = string + letter
collect(node.hash[letter], stringn)
Also, you probably want to either always initialize #words to empty in print before calling collect, or make it a local variable in print and pass it to collect.

Related

Converting Ruby Hash into string with escapes

I have a Hash which needs to be converted in a String with escaped characters.
{name: "fakename"}
and should end up like this:
'name:\'fakename\'
I don't know how this type of string is called. Maybe there is an already existing method, which I simply don't know...
At the end I would do something like this:
name = {name: "fakename"}
metadata = {}
metadata['foo'] = 'bar'
"#{name} AND #{metadata}"
which ends up in that:
'name:\'fakename\' AND metadata[\'foo\']:\'bar\''
Context: This query a requirement to search Stripe API: https://stripe.com/docs/api/customers/search
If possible I would use Stripe's gem.
In case you can't use it, this piece of code extracted from the gem should help you encode the query parameters.
require 'cgi'
# Copied from here: https://github.com/stripe/stripe-ruby/blob/a06b1477e7c28f299222de454fa387e53bfd2c66/lib/stripe/util.rb
class Util
def self.flatten_params(params, parent_key = nil)
result = []
# do not sort the final output because arrays (and arrays of hashes
# especially) can be order sensitive, but do sort incoming parameters
params.each do |key, value|
calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s
if value.is_a?(Hash)
result += flatten_params(value, calculated_key)
elsif value.is_a?(Array)
result += flatten_params_array(value, calculated_key)
else
result << [calculated_key, value]
end
end
result
end
def self.flatten_params_array(value, calculated_key)
result = []
value.each_with_index do |elem, i|
if elem.is_a?(Hash)
result += flatten_params(elem, "#{calculated_key}[#{i}]")
elsif elem.is_a?(Array)
result += flatten_params_array(elem, calculated_key)
else
result << ["#{calculated_key}[#{i}]", elem]
end
end
result
end
def self.url_encode(key)
CGI.escape(key.to_s).
# Don't use strict form encoding by changing the square bracket control
# characters back to their literals. This is fine by the server, and
# makes these parameter strings easier to read.
gsub("%5B", "[").gsub("%5D", "]")
end
end
params = { name: 'fakename', metadata: { foo: 'bar' } }
Util.flatten_params(params).map { |k, v| "#{Util.url_encode(k)}=#{Util.url_encode(v)}" }.join("&")
I use it now with that string, which works... Quite straigt forward:
"email:\'#{email}\'"
email = "test#test.com"
key = "foo"
value = "bar"
["email:\'#{email}\'", "metadata[\'#{key}\']:\'#{value}\'"].join(" AND ")
=> "email:'test#test.com' AND metadata['foo']:'bar'"
which is accepted by Stripe API

Ruby Sinatra storing variables

In the code below, the initial get '/' contains a form, whose action is post '/'. when the user inputs a number, it should be converted to a variable that will be used to call the Game class, for which I have generated another action to reveal a new form at get '/game'. the variable generated in the post method is not being stored. how can I both store the variable created in post and then link into the get '/game' action?
require 'sinatra'
require 'sinatra/reloader'
##count = 5
Dict = File.open("enable.txt")
class Game
attr_accessor :letters, :number, :guess, :disp
##count = 5
def initialize (number)
letters = find(number)
end
def find (n)
words =[]
dictionary = File.read(Dict)
dictionary.scan(/\w+/).each {|word| words << word if word.length == n}
letters = words.sample.split("").to_a
letters
end
def counter
if letters.include?guess
correct = check_guess(guess, letters)
else
##count -= 1
end
end
end
get '/' do
erb :index
end
post '/' do
n = params['number'].to_i
#letters = Game.new(n)
redirect '/game'
end
get "/game" do
guess = params['guess']
letters = #letters
if guess != nil
correct = check_guess(guess, letters)
end
disp = display(letters, correct)
erb :game, :locals => {:letters => letters, :disp => disp}
end
def display(letters, correct)
line = "__"
d=[]
letters.each do |x|
if correct == nil
d << line
elsif correct.include?x
d << x
else
d << line
end
end
d.join(" ")
end
def check_guess(guess, letters)
correct = []
if guess != nil
if letters.include?guess
correct << guess
end
end
correct
end
You cannot do this:
#letters = Game.new(n)
each time you create a request, and new Request instance created and so the #letters attribute no longer exists.
It's the equivalent of
r = Request.new()
r.letters = Game.new()
r = Request.new()
r.letters # not defined anymore!!
You could achieve what you want using a class variable instead
##letters = Game.new(n)
Although this will become a nightmare when you have multiple users and will only work when you have a single ruby server process.
A more advanced approach would be to store params['number'] in a session cookie or in a database.

Reverse words of a string in Ruby?

I am trying to reverse the words of a string in Ruby, without using the reverse method. I want to implement the known algorithm of:
Reverse the whole string
Reverse each word in the reversed string.
Here is what I have come up with:
class String
def custom_reverse(start, limit)
i_start = start
i_end = limit - 1
while (i_start <= i_end)
tmp = self[i_start]
self[i_start] = self[i_end]
self[i_end] = tmp
i_start += 1
i_end -= 1
end
return self
end
def custom_reverse_words
self.custom_reverse(0, self.size)
i_start = 0
i_end = 0
while (i_end <= self.length)
if (i_end == self.length || self[i_end] == ' ')
self.custom_reverse(i_start, i_end)
i_start += 1
end
i_end += 1
end
end
end
test_str = "hello there how are you"
p test_str.custom_reverse_words
But the results are "yahthello ow ou er ereh"
What am I missing?
The gist of any reverse operation is to iterate over elements in the reverse order of what you'd normally do. That is, where you'd usually use the set (0..N-1) you'd instead go through (N-1..0) or more specifically N-1-i where i is 0..N-1:
class String
def reverse_words
split(/\s+/).map{|w|wl=w.length-1;(0..wl).map{|i|w[wl-i]}.join}.join(' ')
end
end
puts "this is reverse test".reverse_words.inspect
# => "siht si esrever tset"
The same principle can be applied to the words in a given string.
Interview questions of this sort are of highly dubious value. Being "clever" in production code is usually a Very Bad Idea.
Here's one way to reverse an array without using the built-in reverse:
class Array
def reverse
tmp_ary = self.dup
ret_ary = []
self.size.times do
ret_ary << tmp_ary.pop
end
ret_ary
end
end
%w[a b c].reverse # => ["c", "b", "a"]
tmp_ary.pop is the secret. pop removes elements from the end of the array.
The cleanest solution I could think of is:
class Array
def my_reverse
sort_by.with_index {|_, i| -i}
end
end
class String
def words
split(/\W+/)
end
def revert_words
words.my_reverse.join(' ')
end
def revert_each_word
words.map {|w| w.chars.my_reverse.join}.join(' ')
end
end
Once you define a simple and efficient array reverser:
def reverse_array(a)
(a.length / 2).times {|i| a[i],a[-(i+1)] = a[-(i+1)],a[i]}
a
end
You can reverse a sentence pretty straightforwardly:
def reverse_sentence(s)
reverse_array(s.split('')).join.split(" ").map{|w| reverse_array(w.split('')).join}.join(" ")
end
reverse_sentence "Howdy pardner" # => "pardner Howdy"
Here's another way:
class String
def reverse_words
split.inject([]){|str, word| str.unshift word}.join(' ')
end
def reverse_chars
each_char.inject([]){|str, char| str.unshift char}.join('')
end
end
Revised
Carey raises a good point, reverse_chars can be simplified, since string is already an Enumerable:
class String
def reverse_chars
each_char.inject(""){|str, char| str.insert(0, char) }
end
end

Check if two linked lists are equal in Ruby?

I have the following implementation of a linked list in Ruby:
class Node
attr_accessor :data, :next
def initialize(data = nil)
#data = data
#next = nil
end
end
class LinkedList
def initialize(items)
#head = Node.new(items.shift)
items.inject(#head) { |last, data| #tail = last.next = Node.new(data) }
end
def iterate
return nil if #head.nil?
entry = #head
until entry.nil?
yield entry
entry = entry.next
end
end
def equal?(other_list)
#How do I check if all the data for all the elements in one list are the same in the other one?
end
end
I have tried using the .iterate like this:
def equals?(other_list)
other_list.iterate do |ol|
self.iterate do |sl|
if ol.data != sl.data
return false
end
end
end
return true
end
But this is doing a nested approach. I fail to see how to do it.
You can't do it easily with the methods you have defined currently, as there is no way to access a single next element. Also, it would be extremely useful if you implemented each instead of iterate, which then gives you the whole power of the Enumerable mixin.
class LinkedList
include Enumerable # THIS allows you to use `zip` :)
class Node # THIS because you didn't give us your Node
attr_accessor :next, :value
def initialize(value)
#value = value
#next = nil
end
end
def initialize(items)
#head = Node.new(items.shift)
items.inject(#head) { |last, data| #tail = last.next = Node.new(data) }
end
def each
return enum_for(__method__) unless block_given? # THIS allows block or blockless calls
return if #head.nil?
entry = #head
until entry.nil?
yield entry.value # THIS yields node values instead of nodes
entry = entry.next
end
end
def ==(other_list)
# and finally THIS - get pairs from self and other, and make sure all are equal
zip(other_list).all? { |a, b| a == b }
end
end
a = LinkedList.new([1, 2, 3])
b = LinkedList.new([1, 2, 3])
c = LinkedList.new([1, 2])
puts a == b # => true
puts a == c # => false
EDIT: I missed this on the first run through: equal? is supposed to be referential identity, i.e. two variables are equal? if they contain the reference to the same object. You should not redefine that method, even though it is possible. Rather, == is the general common-language meaning of "equal" as in "having the same value", so I changed it to that.
I think there is something wrong with your initialize method in LinkedList, regardless could this be what you need
...
def equal?(other_list)
other_index = 0
cur_index = 0
hash = Hash.new
other_list.iterate do |ol|
hash[ol.data.data] = other_index
other_index += 1
end
self.iterate do |node|
return false if hash[node.data.data] != cur_index
return false if !hash.has_key?(node.data.data)
cur_index += 1
end
return true
end
...
Assuming this is how you use your code
a = Node.new(1)
b = Node.new(2)
c = Node.new(3)
listA = [a,b,c]
aa = Node.new(1)
bb = Node.new(2)
cc = Node.new(3)
listB = [aa,bb,cc]
linkA = LinkedList.new(listA)
linkB = LinkedList.new(listB)
puts linkA.equal?(linkB)

Ruby: Using StringScanner causes infinite loop

I have the following class:
require 'strscan'
class ConfParser
include Enumerable
class Error < StandardError; end
VERSION = '0.0.1'
SECTION_REGEX = /^\[ # Opening bracket
([^\]]+) # Section name
\]$ # Closing bracket
/x
PARAMETER_REGEX = /^\s*([^:]+) # Option
:
(.*?)$ # Value
/x
attr_accessor :filename, :sections
CONFIG_DIRECTORY = "./config"
ENCODING = "UTF-8"
def self.read(filename, opts = {})
new(opts.merge(:filename => filename))
end
def initialize(opts = {})
#filename = opts.fetch(:filename)
#separator = opts.fetch(:separator, ":")
#file = "#{CONFIG_DIRECTORY}/#{#filename}"
#content = nil
#config = Hash.new { |h,k| h[k] = Hash.new }
load
end
def load
raise_error("First line of config file contain be blank") if first_line_empty?
f = File.open(#file, 'r')
#content = f.read
parse!
ensure
f.close if f && !f.closed?
end
def sections
#config.keys
end
def [](section)
return nil if section.nil?
#config[section.to_s]
end
def []=( section, value )
#config[section.to_s] = value
end
private
def parse!
#_section = nil
#_current_line = nil
property = ''
string = ''
#config.clear
scanner = StringScanner.new(#content)
until scanner.eos?
#_current_line = scanner.check(%r/\A.*$/) if scanner.bol?
if scanner.scan(SECTION_REGEX)
#_section = #config[scanner[1]]
else
tmp = scanner.scan_until(%r/([\n"#{#param}#{#comment}] | \z | \\[\[\]#{#param}#{#comment}"])/mx)
raise_error if tmp.nil?
len = scanner[1].length
tmp.slice!(tmp.length - len, len)
scanner.pos = scanner.pos - len
string << tmp
end
end
process_property(property, string)
logger #config
end
def process_property( property, value )
value.chomp!
return if property.empty? and value.empty?
return if value.sub!(%r/\\\s*\z/, '')
property.strip!
value.strip!
parse_error if property.empty?
current_section[property.dup] = unescape_value(value.dup)
property.slice!(0, property.length)
value.slice!(0, value.length)
nil
end
def logger log
puts "*"*50
puts log
puts "*"*50
end
def first_line_empty?
File.readlines(#file).first.chomp.empty?
end
def raise_error(msg = 'Error processing line')
raise Error, "#{msg}: #{#_current_line}"
end
def current_section
#_section ||= #config['header']
end
end
The above class parses files that are setup like so:
[header]
project: Hello World
budget : 4.5
accessed :205
[meta data]
description : This is a tediously long description of the Hello World
project that you are taking. Tedious isn't the right word, but
it's the first word that comes to mind.
correction text: I meant 'moderately,' not 'tediously,' above.
[ trailer ]
budget:all out of budget.
You start running it like this:
require 'conf_parser'
cf = ConfParser.read "/path/to/conf/file"
For some reason when the parse! method runs, an infinite loop occurs and I can't figure out why. Any reason why this would be happening? I have never used StringScanner before, so it may be my lack of knowledge of the class
At the risk of stating the obvious, you are most likely never satisfying scanner.eos?, which in turn would mean that you're not advancing the scan pointer to the end of the string. Since the only change to scanner.pos in the else branch of parse! is to decrement it (i.e. by len), this would be understandable. If the if branch doesn't advance it to the end, you'll never terminate.

Resources