I have an array of instances of e.g. class Result, I want to join names of results rather than results with a ',' like below:
#results=[result1, result2...]
results.join(", ") do |r|
r.name
end
results.join method should be an extensino methods of Array, I want it available to all arrays in my program.
Possible?
Yes, this is possible.
class Array
def join_names
collect(&:name).join(", ")
end
end
But this makes it more likely that you code will have namespace collisions with other libraries that add methods to the Array class.
Related
I have a class called Orders. I want to keep an array of all the "Orders" instances so I can .each map/reduce/or otherwise query them.
Later I would like to Marshal "dump" and "load" the "Orders" array of objects.
My plan is to add each new object created onto an ##all_orders array stack on initialization. Then I can do my .each method on the ##all_orders array, looping through all "orders" objects.
Am I approaching this the right OOP way? (code snippet to get the idea)...
class Orders
##all_orders = Array.new
attr_accessor :order_no, :customer
def initialize(order_no, customer)
#id, #customer = order_no, customer
#order_lines = Array.new
##all_orders << self
end
The proper Object-Oriented way to do this is to have a container you're putting the orders into. This can be a plain-old array or a special container class.
The big problem here is you don't have a proper context for storing the "all orders" data. All orders relevant to what? The entire application? If that's the case you need the concept of an order book or a database to store them.
For example:
class OrderBook < Array
end
order_book = OrderBook.new
order_book << Order.new(...)
Note that I've renamed Orders to Order as that's more accurate, singular name.
Auto-magically adding instances to a container is almost always a recipe for disaster. That sort of thing should be handled using a design pattern like model-controller. It's a huge assumption that you'd want to include it in the global orders pool.
I would consider using dependency injection to pass in a list object to your order instances. Best practice in OOP is to decouple code and try to give each object only one responsibility (the Single Responsibility Principle). This helps keep your code maintainable and easily changeable. The way you're approaching it now, the Order class is doing both order-related stuff AND storage of the list. In my example below, a list object can be passed to the Order object when the order is instantiated and all the order need know is that the list object should respond to the #add method. This way, if the implementation for the list has to change, like making the list object use a Redis store, for example, then you don't need to change both the Order class and the OrderList class. You would only update the OrderList class to deal with Redis in the #add method. Additionally, you no longer have an internal dependency in the Order class for handling list responsibilities, so the order is only doing what it should be.
class OrderList
attr_reader :items
def initialize
#items = []
end
def add(order)
#items << order
end
end
class Order
def initialize(opts)
order_list = opts[:order_list]
order_list.add self
end
end
order_list = OrderList.new
order1 = Order.new order_list: order_list
order2 = Order.new order_list: order_list
puts order_list.list.inspect
I dont know how to set array capacity so my array can store only 3 elements. E.g. if I try to push 4th element, it returns error.
Any ideas?
The default Array class doesn't have that functionality. So, your options are:
Create a separate class that stores the three elements and implement your own methods like push and [].
Subclass Array and override the methods to only allow for three elements, for example:
class ThreeElements < Array
def push(*stuff)
raise 'Already has three elements!' unless length < 3
super
end
end
In my opinion, #1 is the better option, because the default Array interface has too many methods to bother with overriding.
You can create a wrapper class for your array! You can also override the "[]" operator in and check if your index is valid:
def [](i)
# getter
end
def []=(i, v)
# setter
end
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)
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
In C# you can write an extension method like this:
public static Debt[] Foo(this Debt[] arr, int num)
{
// Do something
}
This would allow you to use Foo() on an array of debts: debts.Foo(3)
Can you do this in Ruby? I know you can write a method that will work on arrays:
class Array
def foo
# blah
end
end
but this works on all types of arrays, not just an array of Debts
Thanks in advance.
the extend method is adding the instance methods to a particular object. so in you case it would be:
class YourClass
def foo
# blah
end
end
debts.extend(YourClass)
debts.foo # now foo method is instance method of debts object
Actually it creates singleton class for debts object and then add to it the method. But you can't use this class, that's why it called "ghost" class
This would be a little tricky because Ruby arrays are not homogeneous. That is, you can store different types of objects inside of an array. That would lead me to a solution where I need to first verify that all objects in the array are of type Debt, and if they are then I can act on the array using foo.
You can continue to open up Array and add the foo method, but maybe you should create a FooArray instead and extend Array. This way you can redefine some methods such as << and push to ensure you only take Debts. Since you know that only Debts can be added to your array, you could call foo() without worry.
I believe you can do this by extending Ruby's Array class or better yet defining your own Array-like class and delegating the selected array methods to the native object.
Stack = Array.extract([
:last,
:push,
:pop,
:size,
:clear,
:inspect,
:to_s
])
Why this is better than the alternative methods is explained here.