The #traverse_until method is a helper function that traverses the linked list and returns the node and index of the node once a condition (in a block) is met or until the end of the list. I would greatly appreciate feedback about verbosity, efficiency, readability, etc.
module DataStructures
class Node
attr_accessor :value, :next_node
def initialize(value = nil, next_node = nil)
@value = value
@next_node = next_node
end
end
class LinkedList
attr_accessor :head
def initialize(element = nil)
element.nil? ? @head = nil : @head = Node.new(element)
end
def append(data)
prepend(data) if @head.nil?
current = @head
until current.next_node.nil?
current = current.next_node
end
current.next_node = Node.new(data)
end
def prepend(data)
@head = Node.new(data, @head)
end
def find(data)
result = traverse_until { |node, index| node.value == data }
result[:node].value == data ? result[:index] : "Data not found."
end
def contains? (data)
result = traverse_until { |node, index| node.value == data }
result[:node].value == data
end
def node_at_index(index)
result = traverse_until { |node, i| i == index }
"Node at index #{index}: #{result[:node].value}"
end
def head
@head.nil? ? "Empty list" : @head.value
end
def tail
last_node = traverse_until { |node, index| node.next_node.nil? }
last_node[:node].value
end
def size
last_node = traverse_until { |node, index| node.next_node.nil? }
"List Size: #{last_node[:index] + 1}"
end
def insert_at(data, index)
current = @head
(index - 1).times do |n|
current = current.next_node
end
node_shifted_right = current.next_node
current.next_node = Node.new(data, node_shifted_right)
end
def remove_at(index)
node_after_index = @head
previous = node_after_index
(index + 1).times do |n|
node_after_index = node_after_index.next_node
end
(index - 1).times do |n|
previous = previous.next_node
end
previous.next_node = node_after_index
end
def pop
node_before = nil
current = @head
until current.next_node.nil?
node_before = current
current = current.next_node
end
node_before.next_node = nil
end
def to_s
result = ""
current = @head
loop do
result += "(#{current.value}) -> "
break if current.next_node.nil?
current = current.next_node
end
result += "nil"
end
private
def traverse_until
current = @head
index = 0
until current.next_node.nil? || yield(current, index)
current = current.next_node
index += 1
end
{node: current, index: index}
end
end
end