Remove a node from its old parent's children with Ruby - ruby

BACKGROUND
I am writing a class in Ruby named PolyTreeNode which represents a node in a tree.
STEPS TAKEN
So far, I've been able to make it instantiate a new node where the parent = nil and there are no children. I have also been able to set a new parent and add the self to the new parent's children (unless the self is already included in the array).
PROBLEM
I would like to remove the self from the old parent's children when (re)assigning a new parent.
\
class PolyTreeNode
attr_reader :parent, :children, :value
def initialize(value)
#value = value
#parent = nil
#children = []
end
def parent=(parent)
if parent == nil
#parent = nil
else
#parent = parent
parent.children << self unless parent.children.include?(self)
end
end
\\
The way I wrote it was as follows but I am getting an RSPEC error.
def parent=(parent)
if parent == nil
#parent = nil
else
#parent = parent
parent.children << self unless parent.children.include?(self)
#parent.children.delete(self)
end
end
ยดยดยด
Any ideas? Thanks!

Related

NoMethodError while adding node in Ruby

I am trying to print a linked list in ruby but getting the error add': undefined method `next=' for #<Node:0x000001c5c0738688 #data=1, #next=nil> (NoMethodError)
any idea what i should do ?
class Node
def initialize data
#data = data
#next = nil
end
end
class LinkedList
def initialize
#head = nil
end
def add data
temp = #head
#head = Node.new(data)
#head.next = temp
end
def printList
temp = #head
while temp
puts temp.data
temp = temp.next
end
end
end
list = LinkedList.new
list.add(1)
list.add(2)
list.add(3)
list.printList()
In order to access the setter for the next attribute I'd propose adding an attribute_accessor to Node.
class Node
attr_accessor :next
def initialize(data)
#data = data
end
end
This will generate both a setter and a getter for the next attribute within your Node instance.
Additionally I would also add an attr_reader to Node so that you can access data from outside of the instance.
class Node
attr_accessor :next
attr_reader :data
def initialize(data)
#data = data
end
end

Accessing instance variables from outside the class

I'm having issues understanding this encapsulation. I have instance variables defined with attr_accessor. I'm trying to access them outside class. But they are always nil and returns undefined method.
NoMethodError: undefined method `has_key?' for nil:NilClass
Please help me understand.
class TrieNode
attr_reader :value, :next, :children
def initalize
#value = nil
#children = Hash.new
#next = nil
end
end
class Trie
attr_accessor :root
def initialize
#root = TrieNode.new
end
def build_trie(strs)
cur = #root
strs.each do |str|
str.chars.each do |char|
if cur.children.has_key? char
cur = cur.children[char]
next
else
new_node = TrieNode.new
cur.children[char] = new_node
cur = new_node
end
end
cur = #root
end
end
end
Yes, it is not encapsulation, it is because your object is not instantiated properly, which is caused by a typo
def initalize
#value = nil
#children = Hash.new
#next = nil
end
should be initialize not initalize

How can I use ruby flat_map to map to tuples of children AND parent

Imagine you have an array of objects called parents and each entry is an instance of a Parent. And imagine each parent has a method called children which returns an array of Child instances. Now imagine you want to flat_map from the parents to the children but in a later step you will still need access to the parent (for, say, filtering or whatever). How would you do it?
parents
.flat_map { |parent| parent.children.map { |child| {parent: parent, child: child} } }
Would give me what I need, but... eww. Surely there's a more ruby-esque way of doing this? It almost seems like an RX merge or combineLatest. Can't figure out a ruby way of doing this other than what I already have.
Here's a bit of ruby to generate the parent/child structure I'm talking about using random data:
class Child ; end
class Parent
attr_reader :children
def initialize(children)
#children = children || []
end
end
parents = 100.times.map do
Parent.new(rand(10).times.map { Child.new })
end
Consider adopting the following structure.
class Child
attr_accessor :name # create accessor for name of child
def initialize(name)
#name = name
end
end
class Parent
#families = {} # create class instance variable
class << self # change self to singleton class
attr_reader :families # create accessor for #families
end
def initialize(name, children)
self.class.families[name] = children.map { |name| Child.new(name) }
end
end
Parent.new("Bob and Diane", %w| Hector Lois Rudolph |)
#=> #<Parent:0x00005ac0ddad9aa0>
Parent.new("Hank and Trixie", %w| Phoebe |)
#=> #<Parent:0x00005ac0ddb252c0>
Parent.new("Thelma and Louise", %w| Zaphod Sue |)
#=> #<Parent:0x00005ac0ddcb2890>
Parent.families
#=> {"Bob and Diane" =>[#<Child:0x00005ac0ddadf9f0 #name="Hector">,
# #<Child:0x00005ac0ddadf388 #name="Lois">,
# #<Child:0x00005ac0ddadf1a8 #name="Rudolph">],
# "Hank and Trixie" =>[#<Child:0x00005ac0ddb251d0 #name="Phoebe">],
# "Thelma and Louise"=>[#<Child:0x00005ac0ddcb27c8 #name="Zaphod">,
# #<Child:0x00005ac0ddcb27a0 #name="Sue">]}
Parent.families.keys
#=> ["Bob and Diane", "Hank and Trixie", "Thelma and Louise"]
Parent.families["Bob and Diane"].map { |child| child.name }
#=> ["Hector", "Lois", "Rudolph"]

inserting in linked lists with ruby

Hi I'm trying to insert a value at a specific index in a linked list in ruby.
Here is my code thus far:
class Node
attr_accessor :data, :pointer, :next
def initialize(data, pointer = nil)
#data = data
#pointer = pointer
end
def next
#data = #pointer
end
end
class LinkedList
attr_accessor :head, :data, :pointer
def initialize(data)
#head = Node.new(data, pointer)
end
def index_at(value_of_index)
current = head
value_of_index.times do
if current.pointer == nil
current = Node.new(nil, nil)
return current = current.data
else
current = current.next
end
end
current.data
end
def insert_at_index(index, value)
current = head
index.times do
current.next
end
current = Node.new(value)
end
end
The problem I've having is with the def insert_at_index method... I can't seem to figure out how to place the new node at the index and value. Any help you can give me would be greatly appreciated.
Say your linked list looks like this:
a -> b -> d -> e
To insert c into the 3rd index, you would move to the second index to get b, create a new node, set b's next to the new node c, and set c's next to the old third index item, d.
This will give you:
a -> b -> c -> d -> e
That said, the code should look like this:
def insert_at_index(index, value)
current = head
# make current b. You may want to put this in a function node_at_index
(index - 1).times do
raise "List not long enough" if current.nil?
current = current.next
end
new_node = Node.new(value) # new node c
new_node.next = current.next # c's next is b's next, d
current.next = new_node # b's next is c
end
From the looks of it there seem to be other issues with your code as well. You're overwriting #data in your next function (you probably meant to just return #pointer), LinkedList#initialize doesn't have pointer defined, etc
You could simplify your Node to look like this:
class Node
attr_accessor :data, :next
def initialize(data, next=nil)
#data = data
#next = next
end
end
Which should work for you.
Here is one solution that worked for me:
class Node
attr_accessor :data, :pointer
alias_method :next, :pointer
def initialize(data, pointer = nil)
#data = data
#pointer = pointer
end
def next
#pointer
end
end
class LinkedList
attr_accessor :head, :data, :pointer
def initialize(data)
#head = Node.new(data, pointer)
end
def index_at(value_of_index)
current = head
value_of_index.times do
if current.pointer == nil
current = Node.new(nil, nil)
return current = current.data
else
current = current.next
end
end
current.data
end
def insert_at_index(index, value)
current = head
(index - 1).times do
if current.pointer != nil
current = current.next
end
end
new_node = Node.new(value)
if current.pointer != nil
new_node.pointer = current.pointer
end
current.pointer = new_node
end
end

Best practice to connect objects between them in ruby

I cannot manage to find a nice way to implement the rails behavior of objects navigation rails has in a pure ruby script.
Say I have an object of class Parent, with an accessor to an Array of objects of class Child, I'd like, when I manipulate a Child object, to be able to get the Parent easily, like that :
class Parent
attr_accessor :children
def initialize
#children = Array.new
end
end
class Child
end
parent = Parent.new
first_child = Child.new
second_child = Child.new
parent.children << [ first_child, second_child ]
a_child = parent.children.first
get_parent_from_child = a.child.parent
The part I'm interested in is, of course, the last line in which I attempt to get the "parent" object from one of its children.
How could I implement it easily and cleanly ?
I was thinking of adding an accessor to the child object, but I'm not sure how to make sure this value is set everytime I attach a child object to a parent object.
Is there an easy and clean way of doing that in ruby ?
Thanks in advance.
You don't have to fully expose your children you know, there's nothing wrong with fully controlling access to your data.
class Parent
def initialize
#children = [ ]
end
# You get a shallow copy of the children, you can
# change the individual children but not the tree
# structure.
def children
#children.dup
end
# We take ownership of the children when you add them.
def add_children(*kids)
kids.each { |k| k.parent = self }
#children += kids
end
def remove_children(&block)
#children.delete_if(&block)
end
end
# This would probably have some sort of payload and
# a sensible "==" implementation.
class Child
attr_reader :parent
def parent=(p)
raise StandardError => 'Not allowed to change parents!' if #parent
#parent = p
end
end
Then child.parent works fine and if you want to remove children you'd tell the parent which ones to remove:
# Remove the children with even payloads.
parent.remove_children { |c| c.payload % 2 == 0 }
# Remove the children with odd payloads and copy them.
odds = [ ]
parent.remove_children do |c|
kill_it = c.payload % 2 != 0
odds << Child.new(c.payload) if(kill_it)
kill_it
end
You can achieve the has_many behavior from Rails by using a custom container class, like Rails does.
class Parent
attr_reader :children
def initialize
#children = ChildCollection.new(self)
end
end
class Child
attr_accessor :parent
end
class ChildCollection
def initialize parent
#children = []
#parent = parent
end
def new
child = Child.new
child.parent = #parent
#children << child
child
end
def << child
child.parent = #parent
#children << child
self
end
end
And some example code to add children to the parent:
parent = Parent.new
child1 = parent.children.new
child2 = Child.new
child3 = Child.new
parent.children << child2 << child3
puts "Parent is: #{parent.object_id}"
puts "Child1's parent is: #{child1.parent.object_id}"
puts "Child2's parent is: #{child2.parent.object_id}"
puts "Child3's parent is: #{child3.parent.object_id}"
You may want to throw on some other helpful methods like remove and each (so you can have ChildCollection include the Enumerable module and get all the cool stuff that comes with that) but this should be enough to get you started.
Write an own accessor for children in the class Parent in which you set a parent attribute for each child added to the array. Do the reverse when removing children from the array. This is how I would do it.

Resources