In my application I have positions. Each position has one parent and many children positions. How can I navigate the tree and find all positions that are in the same generation?
Currently I have this:
def org_chart_level
ps = self
level = 10
while ps do
break if ps.reports_to == nil
ps = ps.reports_to
level += 10
end
if self.reports_to
siblings = self.reports_to.responsible_for
else
siblings = [self]
puts siblings
end
"#{level}-#{siblings.index(self) + 1}"
end
This almost works because it can give me the level of a position and it can tell me which sibling it is of the parent, but it cannot tell me which sibling it is in the generation.
Expected level for each position.
1.1
-2.1
-3.1
-2.2
-3.2
Actual level:
1.1
-2.1
-3.1
-2.2
-3.1
I ended up storing the positions generation in the database before it is saved.
before_save :find_generation
def find_generation
ps = self
i = 0
while ps do
break if ps.reports_to == nil
ps = ps.reports_to
i += 1
end
self.generation = i
end
And then when I need to generate the org chart level:
def org_chart_level
ps = self
level = 10
while ps do
break if ps.reports_to == nil
ps = ps.reports_to
level += 10
end
generation = organization.positions.where(generation: self.generation) #position belongs to organization.
"#{level}-#{generation.index(self) + 1}"
end
Related
I have large Json object. The object describes among other things a tree type relation of how its components objects are connected hierarchally. The object knows who its children are, but does not know (directly) who its parent is. "my_hash" below exemplifies the structure. Every object has an id 101, 102, etc., a name "one", "two" etc and it can have 0, 1 or more children. I am trying to build the "path" to the every object. E.g. object name "five" should a result have a path of "/one/two/four" as a result of the code. Basically I am trying to build a sort of directory structure of the hierarchy of the objects.
The code below works, but it looks quite long'ish, not very elegant, not very Ruby'ish.
I would be grate for suggestion on how to to do this more efficiently and elegantly. And I got hunch that my code may not be very robust, ie deal well with exceptions.
Any thoughts or help ar appreciated.
On a side note, I am just learning Ruby and so far have mainly programmed in Perl.
class Tree
def initialize
#my_hash = {
101 => ["one", [102, 107]],
102 => ["two", [103, 104]],
103 => ["three", []],
104 => ["four", [105, 106]],
105 => ["five", []],
106 => ["six", []],
107 => ["seven", [108]],
108 => ["eight", []],
}
#child_to_parent_node = {}
#id_to_name = {}
#my_path_hash = {}
#my_hash.keys.each do |key|
#my_path_hash[key] = ""
end
#parent_path_id = []
end
def map_child_to_parent
#my_hash.each do |key, value|
#id_to_name.store(key, value[0])
node_name, children = value[0], value[1]
children.each do |child_id|
#child_to_parent_node.store(child_id, node_name)
end
end
end
def build_path(id)
parent = #child_to_parent_node[id]
parent.nil? ? return : #parent_path_id << parent
id = #id_to_name.key(parent)
build_path(id)
#parent_path_id
end
def update_tree
#id_to_name.keys.each do |id|
tmp_array = self.build_path(id)
path = ""
if (tmp_array.nil?)
path = "/"
else
tmp_array.reverse.each do
path = path + "/" + tmp_array.pop
end
end
puts "id: #{id} path: #{path}"
end
end
end
my_tree = Tree.new
my_tree.map_child_to_parent
my_tree.update_tree
In fact, to solve this task you have to traverse the tree from the leaf to root, right? So, your representation of the tree is very inconvenient for this particular task. If you are ok to trade off some memory for a more clean solution, I'd create an auxiliary structure that contains parents for each node. Let's say
#parents = #my_hash.each_with_object({}) do |(pid, props), acc|
_, children = props
children.each { |cid| acc[cid] = pid }
end
#=> {102=>101, 107=>101, 103=>102, 104=>102, 105=>104, 106=>104, 108=>107}
Now, the task can be solved in a quite concise way with a couple of auxiliary functions. For example:
def id_by_name(name)
id, _ = #my_hash.find { |k, v| v.first == name }
id
end
def name_by_id(id)
#my_hash[id].first
end
def path_to(node)
path = [node]
id = id_by_name(node)
path.unshift(name_by_id(id)) while id = #parents[id]
path.join("/")
end
path_to "five" #=> "one/two/four/five"
Please, note: the solution is still very inefficient - mostly because to fetch a node's id by its name we have to iterate over the whole initial hash in the worst case. This is the price we pay for the data structure that doesn't fit the task well.
I am learning about speech recognition recently, and I have learned that the idea of prefix beam search is to merge paths with the same prefix, such as [1,1,_] and [_,1,_] (as you can see, _ indicates blank mark).
Based on this understanding, I implemented a version of mine, which can be simplified using pseudo code like this:
def prefix_beam_search(y, beam_size, blank):
seq_len, n_class = y.shape
logY = np.log(y)
beam = [([], 0)]
for t in range(seq_len):
buff = []
for prefix, p in beam:
for i in range(n_class):
new_prefix = list(prefix) + [i]
new_p = p + logY[t][i]
buff.append((new_prefix, new_p))
# merge the paths with same prefix'
new_beam = defaultdict(lambda: ninf)
for prefix, p in buff:
# 'norm_prefix' can simplify the path, [1,1,_,2] ==> [1,2]
# However, the ending 'blank' is retained, [1,1,_] ==> [1,_]
prefix = norm_prefix(prefix, blank)
new_beam[prefix] = logsumexp(new_beam[prefix], p)
# choose the best paths
new_beam = sorted(new_beam.items(), key=lambda x: x[1], reverse=True)
beam = new_beam[: beam_size]
return beam
But most of the versions I found online (according to the paper) are like this:
def _prefix_beam_decode(y, beam_size, blank):
T, V = y.shape
log_y = np.log(y)
beam = [(tuple(), (0, ninf))]
for t in range(T):
new_beam = defaultdict(lambda: (ninf, ninf))
for prefix, (p_b, p_nb) in beam:
for i in range(V):
p = log_y[t, i]
if i == blank:
new_p_b, new_p_nb = new_beam[prefix]
new_p_b = logsumexp(new_p_b, p_b + p, p_nb + p)
new_beam[prefix] = (new_p_b, new_p_nb)
continue
end_t = prefix[-1] if prefix else None
new_prefix = prefix + (i,)
new_p_b, new_p_nb = new_beam[new_prefix]
if i != end_t:
new_p_nb = logsumexp(new_p_nb, p_b + p, p_nb + p)
else:
new_p_nb = logsumexp(new_p_nb, p_b + p)
new_beam[new_prefix] = (new_p_b, new_p_nb)
if i == end_t:
new_p_b, new_p_nb = new_beam[prefix]
new_p_nb = logsumexp(new_p_nb, p_nb + p)
new_beam[prefix] = (new_p_b, new_p_nb)
beam = sorted(new_beam.items(), key=lambda x: logsumexp(*x[1]), reverse=True)
beam = beam[:beam_size]
return beam
The results of the two are different, and my version tends to return longer strings. And I don't quite understand the main two aspects:
Are there any details of my version that are not thoughtful?
The common version while generate new prefix by new_prefix = prefix + (i,) regardless of whether the end of the previous are the same as the given 's'. For example, the old prefix is [a,a,b] and when a new character s is added, both [a,a,b] and [a,a,b,b] are saved. What is the purpose if this? And does it cause double counting?
Looking forward to your answer, thanks in advance!
When you choose the best paths in your code, you don't want to differentiate between [1,_] and [1] since both correspond to the same prefix [1].
If you have for example:
[1], [1,_], [1,2]
then you want the probability of [1] and [1,_] both to have the sum of the two.
probability([1]) = probability([1])+probability([1,_])
probability([1,_]) = probability([1])+probability([1,_])
And after sorting with these probabilities, you may want to keep so many that the number of true prefixes is beam_size.
For example, you have [1], [1,_], [2], [3].
Of which probabilities are: 0.1, 0.08, 0.11, 0.15
Then the probabilities with which you want to sort them are:
0.18, 0.18, 0.11, 0.15, respectively (0.18 = 0.1 + 0.08)
Sorted: [1]:0.18, [1,_]: 0.18, [3]:0.15, [2]:0.11
And if you have beam_size 2, for example, then you may want to keep
[1], [1,_] and [3] so that you have 2 prefixes in your beam, because [1] and [1,_] count as the same prefix (as long as the next character is not 1 - that's why we keep track of [1] and [1,_] separately).
I'm trying to get my save_game() method to work, nothing is getting written to the JSON file. This is my first assignment working with JSON and serialization in general. I'm not quite sure where I'm even going wrong.
These are my serialization methods:
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
This is my program, on line 92 I try to call my save_game method.
require 'json'
load 'display.rb'
class Hangman
attr_accessor :name
#name = name
def initialize
puts "What is your name?"
#name = gets.chomp
puts "
################################################
HANGMAN
################################################
_________
|
| |
| O
| /|\\
| |
| / \\
|
-----------------
Welcome #{#name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
"
end
end
class Gameplay
attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
def initialize
#array = []
#filestuff = File.foreach('5text.txt') do |x|
chomped = x.chomp
#array << chomped if (chomped.length >= 5 and chomped.length <= 12)
end
#random_word = #array.sample
#cipher = #random_word.gsub(/[a-z]/, '*').split(//)
#random_word2 = #random_word.split(//)
#counter = 5
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
def choice(n)
#random_word2.each_with_index do |i,index|
if i == n
#cipher[index] = i
end
end
if n == #random_word2.join.to_s
puts "You win"
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
Hangman.new
else exit
end
end
if #random_word2.include?(n) == false
#counter -= 1
display
puts "#{#counter} guesses remaining."
puts "To save press 1"
save = gets.chomp
if save == "1"
#Will not save
save_game($b.to_json)
end
end
if #counter == 0
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
else exit
end
end
puts #cipher.join
end
#counter = 5
while #counter > 0
choice(gets.chomp)
end
end
end
Hangman.new
$b = Gameplay.new
You need to close the file in order to make sure your output is actually written to the disk ("flushed"). You can manually, call close:
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
or, you can use File.open, which takes a block and closes the file when the block ends:
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
Since, writing to the disk is a slow operation, Ruby (and all languages that I can think of right now) will hold off on actually writing the file until it has accumulated a certain amount of text in a buffer. Once it has reached this limit, it will flush the buffer and write everything in it to disk. In order to make sure all your text is actually written when trying to write a file, you need to call close on the file, and as part of closing it, Ruby will flush whatever is left in its buffer.
There are other ways of making sure your content is flushed but when you're just starting to learn about this stuff, it should suffice to just make sure to always close files when you're done reading or writing them.
I've been having a hard time debugging the memory leak.
I have paging using HTTParty inside Sidekiq, and the memory is keep growing and growing. I profiled the sidekiq and this is what I found:
540 /home/user/.rvm/gems/ruby-2.2.3/gems/aws-sdk-core-2.4.2/lib/seahorse/client/configuration.rb:157:DATA
540 /home/user/.rvm/gems/ruby-2.2.3/gems/aws-sdk-core-2.4.2/lib/seahorse/client/configuration.rb:157:NODE
609 /home/user/.rvm/gems/ruby-2.2.3/gems/activesupport-4.1.14/lib/active_support/core_ext/class/attribute.rb:86:DATA
1376 /home/user/.rvm/gems/ruby-2.2.3/gems/activerecord-4.1.14/lib/active_record/connection_adapters/postgresql/database_statements.rb:148:STRING
1712 /home/user/.rvm/gems/ruby-2.2.3/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:ARRAY
1713 /home/user/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/set.rb:290:STRING
1964 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/types/_columnar.rb:29:OBJECT
1964 /home/user/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/set.rb:72:OBJECT
2572 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/types/_columnar.rb:24:STRING
2987 /home/user/.rvm/gems/ruby-2.2.3/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:NODE
3126 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/types/container.rb:10:OBJECT
3506 /home/user/.rvm/gems/ruby-2.2.3/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:DATA
3928 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/type.rb:532:STRING
3928 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/type.rb:543:STRING
5175 /home/user/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/set.rb:81:HASH
5234 /home/user/.rvm/gems/ruby-2.2.3/gems/activesupport-4.1.14/lib/active_support/dependencies.rb:247:STRING
5892 /home/user/.rvm/gems/ruby-2.2.3/gems/mime-types-3.1/lib/mime/type.rb:546:STRING
8120 /home/user/.rvm/gems/ruby-2.2.3/gems/json-1.8.3/lib/json/common.rb:155:ARRAY
87403 /home/user/.rvm/gems/ruby-2.2.3/gems/json-1.8.3/lib/json/common.rb:155:HASH
541817 /home/user/.rvm/gems/ruby-2.2.3/gems/json-1.8.3/lib/json/common.rb:155:STRING
Notice the json STRING and HASH. These bottom 2 just keeps growing as the sidekiq keeps on running more and more jobs.
The code is this:
...
begin
reactions = HTTParty.get("https://graph.facebook.com/v2.7/#{vid.external_id}/reactions?summary=true&limit=#{PAGE_SIZE}&access_token=#{ENV['at']}")
rescue Exception => ex
return
end
r_last_id = "vl/programs/#{vid.program_id}/vids/#{vid.id}/reactions/last_id"
r_last_entry_idx = "vl/programs/#{vid.program_id}/vids/#{vid.id}/reactions/last_entry_idx"
reactions_purged = []
abort = false
total_records = reactions['summary']['total_count'] || 0
last_total_count = $redis.get(r_last_entry_idx).to_i
need_to_run = total_records - last_total_count
need_to_run = 0 if need_to_run < 0
last_id = nil
loop do
break if reactions['data'].nil? || reactions['data'].empty?
reactions['data'].each do |r|
last_id = $redis.get(r_last_id)
abort = true and break if !last_id.nil? && need_to_run <= 0
need_to_run -= 1
reactions_purged << r
end
GC.start # <----- Even this didnt solve it
break if abort
break if reactions['paging']['next'].nil?
reactions = HTTParty.get(reactions['paging']['next'])
end
...
Even the GC.start I added as you see, didn't solve it.
What causes this JSON leakage? this is coming from HTTParty...
Thanks
I am trying to play with JRuby with Java Swing : taking examples here and there, I am trying to translate them from pure Java to JRuby. That works well, especially after reading the part dedicated to Jruby in the excellent site Zetcode.
However there are things I still don't know how to translate.
For instance, picking this java code from Horstmann book, how could I translate correctly into JRuby ? In this code (in the Jpanel constructor), we rely on internal class for MouseAdapter. The rest is easy.
How to translate such internal (or more adequately 'anonymous') classes ?
just refactor/adjust parts to work out for you e.g.
frame = javax.swing.JFrame.new
frame.title = "MouseTest"
frame.set_size(300, 200)
frame.add_window_listener do |evt|
if evt.getID == java.awt.event.WindowEvent::WINDOW_CLOSING
java.lang.System.exit(0)
end
end
class MousePanel < Java::JavaxSwing::JPanel
SQUARELENGTH = 10; MAXNSQUARES = 100;
def initialize
super
#squares = []; #current = nil
add_mouse_listener self
add_mouse_motion_listener self
end
def add(x, y)
if #squares.size < MAXNSQUARES
#current = #squares.size
#squares << Point.new(x, y)
repaint
end
end
def remove(n)
return if (n < 0 || n >= #squares.size)
#squares.pop
#squares[n] = #squares[#squares.size];
#current = nil if #current == n
repaint
end
def paintComponent(graphics)
super
#squares.each { |square| do_draw(graphics, square) }
end
def do_draw(graphics, square)
graphics.drawRect(
square.x - SQUARELENGTH / 2,
square.y - SQUARELENGTH / 2,
SQUARELENGTH, SQUARELENGTH
)
end
private :do_draw
include java.awt.event.MouseListener
[ 'mouseEntered', 'mouseExited', 'mouseReleased' ].each do |method|
class_eval "def #{method}(evt); end"
end
def mousePressed(evt)
puts "mousePressed #{evt}"
end
def mouseClicked(evt)
puts "mouseClicked #{evt}"
end
include java.awt.event.MouseMotionListener
def mouseMoved(evt)
puts "mouseMoved #{evt}"
end
def mouseDragged(evt)
puts "mouseDragged #{evt}"
end
end
frame.content_pane.add MousePanel.new
frame.show
NOTE all the updated *add_xxx_listener occurrences ...
put this into a gist for readability/forkability (including the original code) : https://gist.github.com/kares/8538048