I have a multi-level tree structure and i'm trying to return an array of ancestors for an object which can have 1-3 ancestors. I have a working method, however it's complex and I would prefer to use a loop, anyone know how I can using ruby?
def ancestors
#a = []
#a.push(parent) if parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
return #a
end
Assuming I understand your classes right.. I was thinking something like this
def ancestors
(parent.present? ? [parent, parent.ancestors] :[]).flatten
end
If Parent is present, it returns an array consisting of parent and its ancestors. The flatten is required because each level adds a array layer.
Off topic. return is considered bad style in ruby, and unless you need it, there is no need for this list to be a member variable.
This is a job for recursion.
You need to make a function which calls itself.
Something like this....
def ancestors
if self.parent.present?
ancestors << self.parent.ancestors
else
return self
end
end
It is fairly simple to do this with an iteration as well, you could try
def ancestors
#a = []
anc=parent
while anc.present? do
#a.push anc
anc=anc.parent
end
return #a
end
(not tried as I do not have your data structure)
Related
This is a problem from a textbook: to take a previously demonstrated tree-generating program and make it generate a tree of instances with names determined by a hash. Code below. There are a number of things I don't understand. First, what is the purpose of the first line? I see that it "defines an instance variable, an accessor, and a setter," but what does that mean? Second, why are we passing around a block for 'puts node.node_name' rather than just putting that in the 'visit' method? Third, that line is the only time that 'node' is referred to in this entire program. It doesn't appear to be defined. So what is it? And finally, what am I doing wrong here? How can I make this work?
-Thanks.
class Day4thing3
attr_accessor :children, :node_name
def initialize (name, children = {})
#children = children
#node_name = name
children.each do
|a,b|
a = Day4thing3.new(a, b)
end
end
def visit_all (&block)
visit &block
children.each_key {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
children = {'grandpa'=>{'dad'=>{'child1'=>{},'child2'=>{}},'uncle'=>{'cousin1'=>{},'cousin2'=>{}}}}
family = Day4thing3.new("family", children)
family.visit_all {|node| puts node.node_name}
end
output:
family
C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:in block in visit_all': undefined methodvisit_all' for "grandpa":String (NoMethodError)
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:in each_key'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:invisit_all'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:36:in <class:Day4thing3>'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:1:in'
First, what is the purpose of the first line? I see that it "defines an instance variable, an accessor, and a setter," but what does that mean?
attr_accessor :children
is a shortcut, equivalent to
def children
#children
end
def children=(value)
#children = value
end
#children is an instance variable, i.e. a variable that each instance of a class can set individually (John has his children, Jane has her children...). A getter is a method that gets the value of an instance variable. A setter is a method that sets it. After this, you can write
p node.children # uses the getter
node.children = [] # uses the setter
Second, why are we passing around a block for 'puts node.node_name' rather than just putting that in the 'visit' method?
Because that makes visit generic. You can visit all nodes and print them, you can visit all nodes and sum them, etc etc. If you hardcode a single purpose into visit, then you would need to code a new function every time you think of a new thing you would like to do to all nodes.
Third, that line is the only time that 'node' is referred to in this entire program. It doesn't appear to be defined. So what is it?
A block is like a function: it has input parameters and a return value. node happens to be the input parameter of the block { |node| puts node.node_name }. So, its value will be whatever the yield or call passes to the block; in this case, the block is invoked by block.call self, so node inside the block is the value of self inside the #visit method.
And finally, what am I doing wrong here?
Before that... let's talk about naming. Day4thing3 is a horrible class name. By all means, save it in a file called day4thing3.rb if you want; if you want to namespace it, put it into a module named Day4Thing3; but please name the class Node, or Tree, or something meaningful. Because this makes no sense:
A Day4thing3 assumes its children are also a Day4thing3.
but this does:
A Node assumes its children are also a Node.
Anyway - you try to change the hash keys into Node objects, but a is just a parameter - assigning to it will not change the key in the hash. You could use Hash#transform_keys for that. However, you are not using values at all after the constructor, so changing #children into an Array would make more sense.
So when you visit_all, you visit the current node, then visit_all the child nodes. This works on your "family" Node: you print the node name ("family") and then try to visit all the children keys. However, the key is "grandpa", which is a String and not a Node (remember, a = Node.new(a, b) did not change the hash, only the local variable a), so the function fails.
A final point, regarding style: method names should not be separated from the parentheses by a space. It works for definition, it breaks for invocation; using "no space" rule makes it consistent.
So, with all that:
module Day4Thing3
class Node
attr_accessor :children, :node_name
def initialize(name, children={})
#node_name = name
#children = children.map do |child_name, child_children|
Node.new(child_name, child_children)
end
end
def visit_all(&block)
visit &block
children.each { |child| child.visit_all &block }
end
def visit(&block)
block.call self
end
end
children = {'grandpa'=>{'dad'=>{'child1'=>{},'child2'=>{}},'uncle'=>{'cousin1'=>{},'cousin2'=>{}}}}
family = Node.new("family", children)
family.visit_all {|node| puts node.node_name}
end
So im going through the OdinProject and starting on the "BinaryTree" portion. I've written a Binary Tree but only in C++ using pointers. So without pointers it's a little more confusing to me at how the nodes connect:
class Node
attr_accessor :value, :parent, :left, :right
##nodes = 0
def self.nodes
##nodes
end
def initialize(value, parent=nil)
#value = value
#parent = parent
#left = nil
#right = nil
##nodes += 1
end
end
class BinaryTree
def initialize
#root = nil
end
def build_tree(arr)
arr.each {|el| add_child(el,#root)}
#root
end
def add_child(value,node)
if #root.nil?
#root = Node.new(value)
else
if value < node.value
node.left.nil? ? node.left = Node.new(value,node) : add_child(value,node.left)
elsif value > node.value
node.right.nil? ? node.right = Node.new(value,node) : add_child(value,node.right)
end
end
return node
end
end
I've done some basic print functions, and it SEEMS to be working correctly, but I was wondering if this looks correct to others that understand ruby better. (Sidenote: I realize this does not handle duplicate numbers). Am I missing something with the "building" portion of the tree.
The reason im concerned is a see a lot of different "build" implementations. One that starts at the midpoint for instance, or is this just for "Sorted" arrays?
welcome to the ruby world! Beware you might really like it and never come back to another language :)
Here are some comments I can give you:
Testing
First thing when you wonder if your code is correct is to test it with different inputs. You can do it by hand, in the console, or you can write tests that will follow you during the development.
By printing
A quick way of testing what you do is to define the to_s method on your Node class. For instance, this will allow to check your tree is ordered:
class Node
…
def to_s
"#{left} #{value} #{right}".strip
end
end
to_s is the default ruby method used when converting an object to a string. You can for instance do:
#> puts BinaryTree.new.build_tree([5, 9, 1, 6])
1 5 6 9
By testing
For your peace of mind, you can describe some tests that will allow you to write code, and modify it, and check it is still working. I would suggest minitest for doing it easily.
require 'minitest/autorun'
describe BinaryTree do
before do
#constructor = BinaryTree.new
end
describe 'when given an array' do
it 'sorts it' do
#constructor.build_tree([1, 3, 2]).to_s.must_equal '1 2 3'
end
end
describe 'when given an array with duplicates' do
it 'builds the correct tree' do
#constructor.build_tree([1, 3, 2, 3]).to_s.must_equal '1 2 3 3'
end
end
end
You can then run it with ruby -Ilib:test binary_tree.rb if binary_tree.rb is the file where you put your code.
You will see there, as you mentioned, that the duplicate code doesn’t work.
You can make it work by removing a condition in the if…elsif block.
Then you wan work on the code and run the test as often as you like so that you are confident you didn’t break anything.
Each time you have an edge case you are not sure your code handles, just put that in a test.
Counting the nodes
Your Node.nodes method is probably not what you want if it is supposed to count the number of nodes in your binary tree.
It should be in the instance of a Node, not in the class itself: if you have several trees, all nodes are taken into account for each tree.
Ruby things
Some of the things you write could be expressed differently in ruby:
Nil is not necessary
attr_accessor parent is syntactic sugar for
def parent
#parent
end
def parent=(other)
#parent = other
end
In ruby if your instance variable #parent is not declared, it has the value nil by default, and calling node.parent will return nil too.
You don’t need these 2 lines in the initializer:
#left = nil
#right = nil
Return is not necessary
When your last instruction in a method is a return, you don’t need the return keyword. That is what I did with the to_s method above.
Named parameters
I think it is cleaner to have named parameters when they are not obvious.
For instance, calling Node.new(value, node) doesn’t really help understanding what are these 2 parameters for. I get that a node should have a value, but what is this second parameter?
You could define:
def initialize(value, parent: nil)
# Same as before
end
and call it with Node.new(value, parent: node), or Node.new(value)
OO things
Moving methods where they belong
Your BinaryTree#add_child method should be on a node. You are adding a child to the node. Move it there, so you don’t need your second parameter:
add_child(value, node.right) becomes node.right.add_child(value)
class Node
...
def add_child(other_value)
if other_value < value
left.nil? ? self.left = Node.new(other_value, parent: self) : left.add_child(other_value)
else
right.nil? ? self.right = Node.new(other_value, parent: self) : right.add_child(other_value)
end
end
end
class BinaryTree
...
def add_child(value)
if #root.nil?
#root = Node.new(value)
else
#root.add_child(value)
end
#root
end
end
While you are there you could also get rid of build_tree in the BinaryTree class, and move it to the Node.
I hope it will help you in your Ruby Journey.
I have a class like this:
class Tiles
attr_accessor :board
def initialize
#board = Array.new(4) { Array.new(4) { 0 } }
end
...
Later, I call this method:
def display_board
padded_board = #board.clone
padded_board.each_with_index do |row, x|
row.each_with_index do |item, y|
padded_board[x][y] = pad_number(item)
end
puts row.join ' '
end
end
Anytime I change padded_board, #board gets changed as well. I can't figure out why this is happening. Any ideas?
That is because when you clone, you are creating a new instance of an array #board, but its elements, which are also arrays, are not being replaced with new instances. You are not going deeply enough to clone the end elements. You need to do deep clone. You might want to look at some answers and gems suggested here or here.
The reason is that array is in fact a set of pointers to other objects, in your case those objects are another arrays. When you are cloning array, you are creating new pointers, which points to the same objects. By calling padded_board[x][y]= you are modifying the padded_board[x] array, which is referenced by both #board and padded_board.
To fix it, you need to duplicate not only the array, but also each element of this array. Int his case this should be sufficient:
padded_board = #board.map(&:clone)
In more general case it is useful to extend Array with deep_dup method. Rails have this method defined within ActiveSupport module, whcih can also be used in any non-rails project by adding `require 'active_support/core_ext/object/deep_dup'.
I have a class that represents a collection. I included the Enumerable module into it and defined the method #each, so that I get all its methods.
But the problem is that Enumerable's methods don't keep the same class. So, for example, if my class is named Collection, and if I do Collection#select, I would like that the result's class is also Collection (instead of Array). Is there a way how to achieve this?
Since Enumerable#select is designed to return an array, you need to tell somewhere how to map that to a Collection instance. That means, you explicitly need to define Collection#select. Otherwise Ruby will not know the mapping rule from the original array result of Enumerable#select to a Collection instance.
Unfortunately, Ruby's Collection Operations are not type-preserving. Every collection operation always returns an Array.
For collections like Sets or Trees, this is merely annoying, because you need to always convert them back into the type you want to have. But for example for an infinite lazy stream of all prime numbers, this is catastrophic: your program will either hang or run out of memory trying to construct an infinitely large Array.
Most Collection APIs either eliminate duplicate code or are type-preserving, but not both. E.g. .NET's Collection API mostly eliminates duplicate code, but it always returns the same type: IEnumerable (equivalent to Ruby's Enumerator). Smalltalk's Collection API is type-preserving, but it achieves this by duplicating all Collection Operations in every Collection type.
The only Collection API which is type-preserving yet eliminates duplication is Scala's. It achieves this by introducing the new concept of Collection Builders, which know how to efficiently construct a Collection of a specific type. The Collection Operations are implemented in terms of Collection Builders, and only the Collection Builders need to be duplicated … but those are specific to every Collection anyway.
If you want type-preserving Collection Operations in Ruby, you need to either duplicate all Collection Operations in your own Collection (which would be limited to your own code), or redesign the entire Collection API to use Builders (which would require a major redesign of not only your own code but also the existing Collections including every third-party Collection ever written).
It's clear that the second approach is at least impractical if not impossible. The first approach also has its problems, though: Collection Operations are expected to return Arrays, violating that expectation may break other people's code!
You can take an approach similar to Ruby 2.0's lazy collection operations: you could add a new method preserve_type to your API which returns a proxy object with type-preserving Collection Operations. That way, the departure from the standard API is clearly marked in the code:
c.select … # always returns an Array
c.preserve_type.select … # returns whatever the type of c is
Something like:
class Hash
def preserve_type
TypePreservingHash.new(self)
end
end
class TypePreservingHash
def initialize(original)
#original = original
end
def map(*args, &block)
Hash[#original.map(*args, &block)
# You may want to do something more efficient
end
end
Another way could be to make Collection a proxy for the underlying array:
class Collection
def initialize( items= nil )
#items = items || []
end
def respond_to_missing?(method_name, include_private = false)
Enumerable.instance_methods.include? method_name
end
def method_missing name, *args, &block
if #items.respond_to? name
res = #items.send name, *args, &block
res.kind_of?( Array ) ? Collection.new(res) : res
else
super
end
end
end
in IRB:
col = Collection.new [1,2,3]
=> #<Collection:0x0000010102d5d0 #items=[1, 2, 3]>
col.respond_to? :map
=> true
col.map{|x| x * 2 }
=> #<Collection:0x000001009bff18 #items=[2, 4, 6]>
The following worked for me. I found only the filtering methods needed to be redefined. If we redefine all methods that return Array, this includes collect which should not be redefined.
include Enumerable
def select(&block)
self.class.new(super.select(&block))
end
def reject(&block)
self.class.new(super.reject(&block))
end
I have an issue trying to update a nested Hash using a key.
The nested hash I have is like this:
main_hash = {
"Energy"=>
{"name"=>"Energy", "uri"=>"energy",
"children"=>
{"Renewable Energy"=>{"name"=>"Renewable Energy", "uri"=>"energy/renewable_energy"}}}
,
"Farming"=>
{"name"=>"Farming", "uri"=>"farming",
"children"=>
{"Organic Gas"=>
{"name"=>"Organic Gas", "uri"=>"farming/organic_gas"
"children" =>
{"Gas Oil"=>{"name"=>"Gas Oil", "uri"=>"farming/organic_gas/gas_oil"}}
}}}}
What I would like to do is to update an item from the hash (e.g., I want to add another child to "Organic Gas"). I know I can do this:
main_hash["Farming"]["children"]["Organic Gas"]["children"].merge!(another_hash)
The problem is I need to get that dynamically as it can get quite deeply nested.
So to get to the desired level, I would do this (which does work as above).
main_hash.send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")
It would really be great if I could call "send" method dynamically like below (obviously it won't work).
main_hash.send(:try, 'send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")')
I hope it makes it clear what I want to achieve. I have gone through all Ruby Hash's built in function and I can't get the one suited for my need.
Any help would be greatly appreciated.
Cheers.
I'm not sure a Hash is really the best data structure here. You're trying to use it to represent a tree, which is fine and all, but it might be a bit clearer if you just explicitly made it a tree:
class Tree
attr_reader :name, :uri, :children, :parent
def initialize(name, uri, *children)
#children = children
#name, #uri = name, uri
end
def <<(child)
#children << child
end
def find(name)
each_branch.detect {|branch| branch.name == name }
end
def leaf?
#children.empty?
end
# The parameter `i` just decides whether or not to include self.
def each_branch( i=true, &blk )
enum = Enumerator.new do |y|
y.yield self if i
#children.each do |c|
next unless c.is_a? Tree
y.yield c
c.each_branch( false ).each {|b| y.yield b }
end
end
block_given? ? enum.each( &blk ) : enum
end
# This yields each leaf and its parent.
def each_leaf( parent=self, &blk )
enum = Enumerator.new do |y|
#children.each do |c|
if !c.leaf?
c.each_leaf( c ).each do |l,p|
y.yield l, p
end
else y.yield c, parent
end
end
end
block_given? ? enum.each( &blk ) : enum
end
end
(I just borrowed those enumerators from a tree structure I'd made before - the each_leaf method might be helpful too, and you can check for the class not being Tree instead of leaf? returning true if you had a tree structure that could contain other objects, like strings).
Then you could do:
root_tree = Tree.new "Farming", "farming"
root_tree << Tree.new( "Organic Gas", "organic_gas" )
gas = root_tree.find "Organic gas"
gas << Tree.new(...)
I think this is just a case of finding the right data structure for the job. Even if the tree method were a bit less efficient, it's clearer what's going on, and will probably lead to fewer bugs in your dynamic code down the track.
If the problem is that you don't want the original tree to be modified, only copied, then just redefine:
class Tree
attr_accessor :children
def <<(child)
new_tree = self.dup
new_tree.children = #children + [child]
new_tree
end
end
That way, you retain the original tree but return a new tree with the extra child added.
To answer the original question, you can use the XKeys gem to traverse a dynamic path as follows:
require 'xkeys' # on rubygems.org
main_hash.extend XKeys::Hash
path = ['Farming', 'children', 'Organic Gas', 'children']
main_hash[*path].merge!(another_hash)
I would also recommend using :children instead of 'children' to avoid having huge numbers of duplicate strings.