Accessing Elements? - ruby

I defined my own method to access elements as:
class Array2
def [](key)
if key.kind_of?(Integer)
#elements[key]
else
# ...
end
end
end
If I had previously declared #elements as Array.new, both the operations:
list = Array2.new
# ...
puts list[0]
puts list.[](0)
work properly. Why is the first operation acceptable?

Both the list[0] and list.[](0) syntaxes mean the exact same thing. They call the [] method with an argument 0 on the list object.

Related

Ruby method doesn't get array parameter

describe Array do
describe "#sum" do
it "has a #sum method" do
expect([]).to respond_to(:sum)
end
it "should be 0 for an empty array" do
expect([].sum).to eq(0)
end
it "should add all of the elements" do
expect([1,2,4].sum).to eq(7)
end
end
end
the code above is the test code given to me. and I made the code below to be tested by the test code.
class Array
def initialize(arr)
#arr = arr
end
def sum
if #arr == nil
return 0
else
numArr = #arr
total = 0
numArr.each {|num|
total += num
}
return total
end
end
end
I thought it would return total 7. (1+2+4 = 7) but it returns 0... I guess it doesn't take [1,2,4] array as a parameter. what am I doing wrong?
As the existing array class already has an initializer, and self inside the instance of an array already is an array, you don't need to add your own initializer
class Array
def sum
self.inject(0){|sum,x| sum + x }
end
end
should do what you want. Lookup inject if it isn't clear, but it does essentially the same that you tried to do with your code, just that you were using a local variable to store the sum.
If this is not only an experiment however I recommend not do monkey patches on core classes if avoidable (and it usually is avoidable). Here's an article with some hints how to do it a bit cleaner, if not avoidable: http://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/

How can I rewrite this flatten method without using class level variables?

I am working on building the flatten method in Ruby. Here is my code:
require 'byebug'
class Array
##new_array = []
def new_flatten
self.each do |element|
if !element.is_a? Array
##new_array << element
elsif element.is_a? Array
element.new_flatten
end
end
##new_array
end
end
test_array = ([1,2,3,[4,5,6,[7]]])
puts test_array.new_flatten == test_array.flatten
My question is this. Do I need to use the class level variable? I would like to use an instance variable but it doesn't seem to get called when I do:
test_array = []
or
Array.new([])
So I had to create a class-level variable to hold the new flattened array.
The problem with using a class variable is that the same one is visible to all the instances, and new_flatten is an operation for an instance. You should refactor your new_flatten algorithm so it doesn't rely on what is, in effect, a "global" variable for the method calls.
For example, you can use a local variable and Ruby's facility to append arrays with +:
class Array
def new_flatten
a = []
self.each do |element|
if element.is_a? Array
a += element.new_flatten
else
a << element
end
end
a
end
end
Also, as some tweaks of style, when doing an if-else, it's often clearer to state the positive logic first. And, you don't need the elsif in this case since the else is the complement of the if. So rather than:
if not something
do_stuff
elsif opposite_of_not_something
do_other_stuff
end
It's clearer to say:
if something
do_stuff
else
do_other_stuff
end
Your question is really confusing. In your title, you talk about calling initialize, but there is no initialize in your code. In your question, you talk about calling an instance variable, but you can't call variables, only methods.
However, implementing flatten is just a simple fold, I don't see the need for storing any intermediate state:
class Array
def new_flatten
inject([]) {|acc, el| acc + Array(case el when Array then el.new_flatten else el end) }
end
end

Undefined method for array

So I'm receiving the error message "undefined method 'ip_histogram' for # (NoMethodError)" with the following code
class CommonLog
def initialize(logfile)
#logfile = logfile
end
def readfile
#readfile = File.readlines(#logfile).map { |line|
line.split()
}
#readfile = #readfile.to_s.split(" ")
end
def ip_histogram
#ip_count = 0
#readfile.each_index { |index|
if (#readfile[index] =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ )
puts #readfile[index]
puts #ip_count += 1
end
}
end
end
my_file = CommonLog.new("test_log")
cleaned_file = my_file.readfile
puts cleaned_file.ip_histogram
I'm trying to read the array in and check it with regex. It works if I take the code out of the method and just throw it into the main program, but I'd like to know why it doesn't work as a method.
You're assigning the results of CommonLog#readfile to a new variable cleaned_file. You didn't supply any input but I'm going to assume #readfile is an array at this point.
That means your code is going to assume there's an instance method ip_histogram on the Array class when in fact there is not.
You'll simply want to run these in sequence to get the result you're after:
clog = CommonLog.new("test_log")
clog.readfile
clog.ip_histogram
The primary difference here is that we're not using the return value of readfile as it only needs to set the variable in the method for ip_histogram to work.
The return value of my_file.readfile is an array. Unless you defined ip_histogram in Array class, you cannot call it on cleaned_file.
To define ip_histogram on Array (with different definition than in your code):
class Array
def ip_histogram
...
end
end

Custom Ruby Enumerator supporting offset

I have the following Query class that behaves like an Enumerator
class Query
include Enumerable
# ...
def each(&block)
#results.each do |id|
yield #repo.find(id)
end
end
end
With the above code I can build the following loops:
query_all.each do |x|
...
query_all.each_with_index(1) do |x, idx|
Just defining the each function, I got for free each_with_index. However, what I cannot get working is this:
query_all.each.with_index(1) do |x, idx|
puts x
end
Failure/Error: query_all.each.with_index(1) do |tag, idx|
LocalJumpError:
no block given (yield)
As far as I know the LocalJumpError is usually related with a missing block (which I am providing), so I am not sure if I am missing some parameter in my each, or if this time I must define a new function.
That happens because you're defining a block inside the each, which yields inside. Each can be called without a block (which is what you're doing after). You should just return the each result (without invoking the block) if the method is called without a block.
class Query
include Enumerable
# ...
def each(&block)
results = #results.map {|id| #repo.find(id)}
if block
results.each(&block)
else
results.each
end
end
end

How can I make the set difference insensitive to case?

I have a class in which the data is stored as a set and I want to be able to compare objects of that class such that the letter case of the elements is of no matter. For example if the set contains elements that are strings there should be no difference of "a" and "A".
To do this I have tried to define the eql? method of the set members to be insensitive to case but this has no effect on the method - (alias difference) in Set. So, how should I go about to make - insensitive to case?
The following code illustrates the problem:
require 'set'
class SomeSet
include Enumerable
def initialize; #elements = Set.new; end
def add(o)
#elements.add(o)
self
end
def each(&block) # To enable +Enumerable+
#elements.each(&block)
end
def difference(compared_list)
#elements - compared_list
end
end
class Element
attr_reader :element
def initialize(element); #element = element; end
# This seems to have no effect on +difference+
def eql?(other_element)
element.casecmp(other_element.element) == 0
end
end
set1 = SomeSet.new
set2 = SomeSet.new
set1.add("a")
set2.add("A")
# The following turns out false but I want it to turn out true as case
# should not matter.
puts set1.difference(set2).empty?
Ok, firstly, you're just storing strings from SomeSet#add, you need to store an instance of Element, like so:
def add(o)
#elements.add(Element.new(o))
self
end
And you need to implement a hash method in your Element class.
You can convert Element##element to lowercase, and pass on its hash.
def hash
element.downcase.hash
end
Full code and demo: http://codepad.org/PffThml2
Edit: For my O(n) insertion comment, above:
Insertions are O(1). From what I can see, eql? is only used with the hash of 2 elements is same. As we're doing hash on the downcased version of the element, it will be fairly well distributed, and eql? shouldn't be called much (if it is called at all).
From the docs:
The equality of each couple of elements is determined according to Object#eql? and Object#hash, since Set uses Hash as storage.
Perhaps you need to implement Object#hash as well.
require 'set'
class String2
attr_reader :value
def initialize v
#value = v
end
def eql? v
value.casecmp(v.value) == 0
end
def hash
value.downcase.hash
end
end
set1 = Set.new
set2 = Set.new
set1.add(String2.new "a")
set2.add(String2.new "A")
puts set1.difference(set2).empty?

Resources