Array fetch or default function - ruby

I need the following logic. If array contains value, return it else return some default value. So far, I've found this can be achieved by using delete:
array.delete(value) || default_value
But, obviously, it modifies the array. Is there a similar function like fetch with default which takes an element instead of an index?
PS. I know this can be rewritten as array.include?(value) ? value : default_value, the question is just for the sake of cleaner code.

Update: There's one way I can think of, but that's harder to read than the ternary:
(array & [value]).first || default_value
You could monkey-patch that into Array if you are so inclined:
class Array
def fetch_value(value, default)
(self & [value]).first || default
end
end
a = *1..3
a.fetch_value(4, "foo")
#=> "foo"
Old answer, before I realized you want to use the value for the lookup, not the index:
Array#fetch takes an optional second argument:
array.fetch(index, default)
Here's an example:
a = *1..3
a.fetch(4, "foo")
#=> "foo"

Related

How to fix end-of-input with two Boolean statements

I'm filtering some Array elements in Ruby and want to filter the positive ones, only if they're all Integers.
l = [1,2,'a','b']
l.select do |number|
new_array = []
new_array.push(number) if number.positive? && number.kind_of? Integer
end
but I got a Syntax error asking for expecting end-of-input.
Why doesn't number.positive? && number.kind_of? Integer work?
The select method is a filtering method and it needs a boolean value that describes if the element should or shouldn't be in the resulting output. In that block you should focus on one thing and one thing only: Describing in minimal terms the acceptance criteria for the filter.
What you're doing here is declaring a local variable, conditionally populating it, then throwing it away, and also discarding the resulting output.
What you really want is to strip this down to the basics:
l = [1,2,'a','b']
filtered = l.select do |number|
number.positive? && number.kind_of? Integer
end
Don't forget to capture the result of this operation or it goes straight in the trash.
There's still a bug here because strings don't have a positive? method, so just reverse the order:
filtered = l.select do |number|
number.kind_of?(Integer) && number.positive?
end
That requires adding brackets to the kind_of? call to avoid ambiguity. && is a very aggressive operator and will interpret the call as this if not properly contained:
number.kind_of?(Integer && number.positive?)
Surround it in parentheses:
new_array.push(number) if number.positive? && (number.kind_of? Integer)
Which results in undefined method `positive?' for "a":String because positive? isn't defined for the strings 'a' and 'b'.
Checking the type first works:
new_array.push(number) if (number.kind_of? Integer) && number.positive?
Just out of curiosity, Monkey patching Object class:
module MyPatches
def integer_and_positive?
kind_of?(Integer) && self > 0
end
end
Object.include MyPatches
For using this way, with Array#keep_if:
l.keep_if(&:integer_and_positive?)
#=> [1, 2]
Note: it alters the original array, for avoiding it use Array#select.

How to handle a select that comes up empty?

How would I handle returning 0 if select comes up empty when searching an array of hashes?
I have the following following line that looks for the created key within an array of hashes:
array.select{|key| key[:created] == Date.yesterday }.first[:amount]
Then, it returns the amount key when it finds the record.
The problem is, if it can't find a record where key[:created] == Date.yesterday then I get this error:
undefined method '[]' for nil:NilClass
Here's what the array of hashes looks like:
[
{:created=>"2014-01-20", :amount=>123},
{:created=>"2014-01-21", :amount=>456},
{:created=>"2014-01-22", :amount=>789}
]
You could use find instead of select, find returns the first object in the enumerable that satisfies the block:
found = array.find { |hash| hash[:created] == Date.yesterday }
amount = found ? found[:amount] : DEFAULT_VALUE
If you are using Rails you could use the try method:
array.find { |hash| ... }.try(:[], :amount)
Update: note that the comparison between a String and a Date is doomed to fail, you have to convert the date to a properly formatted string or vice versa. In my example I've assumed that hash[:created] is a Date object.
The idiom I use is
res = array.select{|key| key[:created] == Date.yesterday }
res.blank? ? nil : res.first[:amount]
Sure, it's not a pretty one liner, but it defends against nil. blank? is I think part of activesupport, but you could roll your own easily.

Ruby - Invoking a class from a CONSTANT that contains the class name

I have a class that calls different suppliers to find if an item is available. How do I execute the class that each constant returns?
class ItemProvider
ADAPTER_ONE = Providers::ItemFromSupplierOne
ADAPTER_TWO = Providers::ItemFromSupplierTwo
def get_item(item)
id = ItemDetail.new(item)
%w(ADAPTER_ONE ADAPTER_TWO).each do |provider|
item_detail = provider.new(id)
break if item_detail.valid?
end
item_detail
end
Your problem is that you aren't making an array that contains the constants' values; you're making an array with the strings "ADAPTER_ONE" and "ADAPTER_TWO". The %w() syntax always makes an array of strings — it doesn't resolve variable names.
What you want is to change your get_item code to something like this:
def get_item(item)
id = ItemDetail.new(item)
[ADAPTER_ONE, ADAPTER_TWO].each do |provider|
item_detail = provider.new(id)
break item_detail if item_detail.valid?
end or nil # break automatically makes the block return the value you break with
end
As an aside, personally, I think I'd rewrite it like this:
def get_item(item)
id = ItemDetail.new(item)
[ADAPTER_ONE, ADAPTER_TWO].map {|provider| provider.new(id) }.find &:valid?
end
Yup you have an array of strings not constants but if you want to go down that road in using classes from strings well it will be nice if you look at http://blog.sidu.in/2008/02/loading-classes-from-strings-in-ruby.html#.UuGdmGQ1i2w .Maybe it is not directly related to your problem but it is a good read.

ruby and references. Working with fixnums

I know a bit about ruby way to handle objects and references. The replace stuff, ect ...
I know it d'ont work on fixnum, cause the var is the fixnum. But i wish to change the value of a fixnum inside a function, and that the value changed in the ouside var.
How can i do this ?
I guess i can use a string like this "1" but that's quite dirty.
Ruby will always pass-by-reference (because everything is an object) but Fixnum lacks any methods that allow you to mutate the value. See "void foo(int &x) -> Ruby? Passing integers by reference?" for more details.
You can either return a value that you then assign to your variable, like so:
a = 5
def do_something(value)
return 1 #this could be more complicated and depend on the value passed in
end
a = do_something(a)
or you could wrap your value in an object such as a Hash and have it updated that way.
a = {:value => 5}
def do_something(dict)
dict[:value] = 1
end
do_something(a) #now a[:value] is 1 outside the function
Hope this helps.
You could pass an array with a single number, like [1], or a hash like {value: 1}. Less ugly than a string, as your number itself remains a number, but less overhead than a new class...
When I was building a game I had the same problem you have. There was a numeric score that represented how many zombies you've killed and I needed to manually keep it in sync between Player (that incremented the score), ScoreBar and ScoreScreen (that displayed the score). The solution I've found was creating a separate class for the score that will wrap the value and mutate it:
class Score
def initialize(value = 0)
#value = value
end
def increment
#value += 1
end
def to_i
#value
end
def to_s
#value.to_s
end
end

Search ruby hash for empty value

I have a ruby hash like this
h = {"a" => "1", "b" => "", "c" => "2"}
Now I have a ruby function which evaluates this hash and returns true if it finds a key with an empty value. I have the following function which always returns true even if all keys in the hash are not empty
def hash_has_blank(hsh)
hsh.each do |k,v|
if v.empty?
return true
end
end
return false
end
What am I doing wrong here?
Try this:
def hash_has_blank hsh
hsh.values.any? &:empty?
end
Or:
def hash_has_blank hsh
hsh.values.any?{|i|i.empty?}
end
If you are using an old 1.8.x Ruby
I hope you're ready to learn some ruby magic here. I wouldn't define such a function globally like you did. If it's an operation on a hash, than it should be an instance method on the Hash class you can do it like this:
class Hash
def has_blank?
self.reject{|k,v| !v.nil? || v.length > 0}.size > 0
end
end
reject will return a new hash with all the empty strings, and than it will be checked how big this new hash is.
a possibly more efficient way (it shouldn't traverse the whole array):
class Hash
def has_blank?
self.values.any?{|v| v.nil? || v.length == 0}
end
end
But this will still traverse the whole hash, if there is no empty value
I've changed the empty? to !nil? || length >0 because I don't know how your empty method works.
If you just want to check if any of the values is an empty string you could do
h.has_value?('')
but your function seems to work fine.
I'd consider refactoring your model domain. Obviously the hash represents something tangible. Why not make it an object? If the item can be completely represented by a hash, you may wish to subclass Hash. If it's more complicated, the hash can be an attribute.
Secondly, the reason for which you are checking blanks can be named to better reflect your domain. You haven't told us the "why", but let's assume that your Item is only valid if it doesn't have any blank values.
class MyItem < Hash
def valid?
!invalid?
end
def invalid?
values.any?{|i| i.empty?}
end
end
The point is, if you can establish a vocabulary that makes sense in your domain, your code will be cleaner and more understandable. Using a Hash is just a means to an end and you'd be better off using more descriptive, domain-specific terms.
Using the example above, you'd be able to do:
my_item = MyItem["a" => "1", "b" => "", "c" => "2"]
my_item.valid? #=> false

Resources