Get the min from an array of instances of multiple classes - ruby

In a Ruby app, I have an array that may contain instances of the Ruby Date class and instances of my MissingDate class.
Now, I need to get the min (or have sort working) from the array. So, I'm looking for the method I need to define in the MissingDate class in order to make min work but that proofs to be more difficult than anticipated.
Any ideas?

According to the documentation you need to implement Comparable.
In your MissingDate include Comparable and define the spaceship operator (<=>).

You simply want to extract the Date objects and then find the earliest.
require 'date'
class MissingDate
end
arr = [Date.parse("17 Apr 21"), MissingDate.new, Date.parse("1 Jan 21"),
MissingDate.new, Date.parse("31 Mar 21")]
arr.grep(Date).min
#=> #<Date: 2021-01-01 ((2459216j,0s,0n),+0s,2299161j)>
If arr contains no Date objects nil is returned.

Related

Ruby - defining class level variable dynamically

I am new to ruby and wanted to know if this is possible. Suppose I have a file with different blocks like this
fruits[tomato=1,orange=2]
greens[peas=2,potato=3]
I have parsed this file and stored it into a hash like this
{"fruits"=>{"tomato"=>"1", "orange"=>"2"}, "greens"=>{"potato"=>"3", "peas"=>"2"}}
And I also know how to access the different parts of the hash. But suppose if want to make it something like this
fruits.tomato # 1
fruits.orange # 2
(Like an object with tomato and orange being its variables)
The catch here is suppose I don't know if the file is going to contain fruits and greens, it could contain a different group called meat. I know this dynamic problem can be solved if I insert everything into a hash with the key as group name and value will be another hash. But can this be done with the example of fruit.tomato or fruits.orange I provided above(Probably by declaring it in a class but I am not sure how to dynamically add class vars in ruby or if that is even possible as I am new to the language).
I spent quite a bit of time making a program just like this in order to help speed up development with API's. I ended up writing a gem to objectify raw JSON (shameless plug: ClassyJSON).
That said, I think your use case is a good one for OpenStruct. I limited my code to just your example and your desired result but here's what it might look like:
require 'ostruct'
hash = {"fruits"=>{"tomato"=>"1", "orange"=>"2"}, "greens"=>{"potato"=>"3", "peas"=>"2"}}
structs = []
hash.each do |k, v|
if v.is_a? Hash
obj = OpenStruct.new({k => OpenStruct.new(v)})
end
structs << obj
end
Here we built up a number of OpenStruct objects and can access their values as you outlined:
[1] pry(main)> structs
=> [#<OpenStruct fruits=#<OpenStruct tomato="1", orange="2">>, #<OpenStruct greens=#<OpenStruct potato="3", peas="2">>]
[2] pry(main)> structs.first
=> #<OpenStruct fruits=#<OpenStruct tomato="1", orange="2">>
[3] pry(main)> structs.first.fruits
=> #<OpenStruct tomato="1", orange="2">
[4] pry(main)> structs.first.fruits.tomato
=> "1"
def add_accessor_method(name, ref)
define_singleton_method name do
return ref
end
end
I found this solution which will make an accessor method for me during parsing the file itself. So I will not have to use OpenStruct later on to convert my hash to an object with different accessor methods. ( I am sure OpenStruct under the hood is doing that)

Match an argument's attribute value in rspec

Is there a way to match an argument's attribute value in rspec? Checking the documentation it looks like there's other matchers available, such as anything, an_instance_of, and hash_including - but nothing to check the value of an object's attribute.
Example - suppose I have this instance method:
class DateParser
def parse_year(a_date)
puts a_date.year
end
end
Then I can write an expectation like this:
dp = DateParser.new
expect(dp).to receive(:parse_year).with(some_matcher)
What I want for some_matcher to check that parse_year is called with any object that has an attribute year that has the value 2014. Is this possible with the out-of-the-box argument matchers in rspec, or does a custom one have to be written?
You can pass a block and set expectations about the argument(s) within the block:
describe DateParser do
it "expects a date with year 2014" do
expect(subject).to receive(:parse_year) do |date|
expect(date.year).to eq(2014)
end
subject.parse_year(Date.new(2014,1,1))
end
end
Maybe something using a double for the passed-in date?
date = double()
date.should_receive(:year)
DateParser.new.parse_year(date)
It's not clear what you mean by the date needing to be 2014. But you could add .and_return(2014) to it to get that behavior from the double.

Ruby Iterator - define a method that accepts an array and a string

I want to define a method that accept an array, and a string, then it should find all the strings in the array that starts with the string that was supplied: example array["Jamaica","Japan","USA","China"]; if the string supplied is Ja, then it should return Jamaica and Japan
Try using keep_if and regex :
["Jamaica","Japan","USA","China"].keep_if { |c| c =~ /^ja/i }
# returns ["Jamaica", "Japan"]
It's a static example. To create the regex dynamically, do Regexp.new("^#{your_var}", true)
The most important methods for an Array are not found in the documentation for Array, but on that for Enumerable. You are searching for a method which finds all elements which comply to some condition.
This condition was that the string starts with some characters. Wouldn't it be nice if a string object had a method for that?
This is overkill but it would work quickly for huge arrays. Lucky for you, I just yesterday stumbled upon a new high speed trie library for ruby called TRIEZ. Run gem install triez then from their example switched up a little bit:
require 'triez'
countries = ["Jamaica","Japan","USA","China"]
t = Triez.new
countries.each do |word|
t[word] = 1
end
ja_countries = []
t.search_with_prefix 'Ja' do |suffix|
ja_countries += "Ja#{suffix}"
end
It's implemented in C so I bet it's fast as hell on huge arrays.
You can do this using many different things, if you don' want to modify the array you can use the select method from Array Class.
["Jamaica","Japan","USA","China"].select{|item| item.match("Ja")}
The Array will be intact.

Ruby array methods for locating based on an object's attribute?

Say I have a Ruby class, Flight. Flight has an attr_accessor :key on it. If there's an array of instances of this class: flights = [flight1, flight2, flight3], I have a "target key", say "2jf345", and I want to find a flight based on it's key, from that array - what sort of code should I use?
This is the code I was going to use:
flights[flights.map { |s| s.key }.index(target_key)]
But it seems like with Ruby, there should be a simpler way. Also, the code above returns an error for me - `[]': no implicit conversion from nil to integer (TypeError). I assume this means that it's not returning an index at all.
Thanks for any help.
You can just use find to get the Flight object, instead of trying to use index to get the index:
flights.find {|s| s.key == target_key }
However your error message suggests that index(target_key) returns nil, which means that you don't actually have a flight with the key you're looking for, which means that find will return nil as well.

Advantages/disadvantages to using struct as a parameter while defining new API

The APIs that I am writing are for a Ruby class that inherits from ActiveRecord. I am trying to write static methods to avoid leaking the ActiveRecord instance. All the APIs now need tuple to uniquely identify a database row.
Is it a good idea to have APIs of the form:
API1(abc, def, ....)
API2(abc, def, ....)
and so on
or should I define a struct with fields to help with future changes?
Any other ideas are greatly welcome!
Using a struct would be strange in Ruby, a Hash would be normal:
def self.api1(options)
# Look at options[:abc], options[:def], ...
end
And then it could be called using named arguments like this:
C.api1 :abc => 'abc', :def => '...'
Easy to extend, common Ruby practice, and easy to make certain parameters optional.
To continue what mu is describing, a common Ruby idiom you'll see it to have a method set itself some default options and then merge into that hash the options that the method received. This way you can be sure that some minimum list of options always exist:
def self.api1(options={})
default_options = { :foo => 'bar', :baz => nil }
options = default_options.merge options
# Now include your code and you can assume that options[:foo] and options[:bar] are there
end
This comes in handy when your method, for example, outputs the value of :baz. Now you don't need to check that it exists first, you can just output it knowing that it would always exist.

Resources