How to access a property of an array in Ruby - ruby

I have this object of class Array
>> answers_to_problem
=> [#<Answer id: 807, problem_id: 1, player_id: 53, code: "function y = times2(x
)\r\n y = 2*x;\r\nend", message: nil, score: 12, output: nil, hide: nil, create
d_at: "2010-02-02 11:06:49", updated_at: "2010-02-02 11:06:49", correct_answer:
nil, leader: nil, success: true, cloned_from: nil>]
For doing a binary check, I need access to the success field. I am not sure I am even using the right terminology here so I can not search how to access it.
answer_to_problems was found this way:
answers_to_problem = Answer.find_all_by_problem_id_and_player_id(current_problem,player_id)
Ultimately, I want to do this check:
is_correct = (answers_to_problem.success == true)

That isn't a property of the array — it's a property of the object in the array. So you'd so answers_to_problem[0].success to access the success attribute of the first object of the array.

Are you sure, you want to use find_all? If you know you'll only get one Answer back, you should use find without the all. That way you get a single Answer object instead of an array.
If you can get back more than one answer, do you want to check that all the answers are successful or just that one of them is?
You can do the former with: answers.all?(&:success) and the latter with answers.any?(&:success).

A bit outside the question here, but:
is_correct = (answer_to_problem.success == true)
Here you are doing an assignment and a truth check which are not really needed.
is_correct is here just reflecting whatever answer_to_problem.success would be. Shorten:
answer_to_problem.success == true
Now you're still performing a comparison to get a boolean value which you already have. Shorten:
answer_to_problem.success
There is a statement which you can use in the same manner you'd use is_correct. To make it read even better you could do:
class Answer
def correct?
success
end
end
And just use answer_to_problem.correct?

Related

Alternative to logical OR, where false is treated as truthy?

So I have some code that gets values from a couple sources in a fixed priority order. It looks like this:
{ foo: true
bar: 42
}.each_pair do |attrib,default|
instance_variable_set :"##{attrib}",data[attrib] || template[attrib] || otherdata[attrib] || default
end
As you can probably tell, trying to override a true default with false won't work. How would I fix this without creating a horrible mess? (I do know how to fix it with a horrible mess, but I'm fairly certain there is something nice in the standard library for this exact situation, I just don't remember what it is called and can't think of a good search term)
If you're looking for the first non-nil value:
instance_variable_set :"##{attrib}", [ data[attrib], template[attrib], otherdata[attrib], default ].compact.first
Not the most efficient method, but gets the job done and works well if not exercised aggressively.
A slightly more forgiving version:
instance_variable_set :"##{attrib}", [ data[attrib], template[attrib], otherdata[attrib], default ].find { |v| !v.nil? }
This scans for the first non-nil value and returns that. It doesn't produce an intermediate array like compact does.
What you really want is a more forgiving method:
def instance_variable_set_from(attrib, *sources)
instance_variable_set(:"##{attrib}", sources.find { |v| !v.nil? }
end
Which is more self-explanatory when used:
instance_variable_set_from(attrib, data[attrib], template[attrib], otherdata[attrib], default)
Assuming that data, template and otherdata are all hashes, you could use fetch. It returns the value if it is contained in the hash, or the block's result otherwise:
value = data.fetch(attrib) { template.fetch(attrib) { otherdata.fetch(attrib, default) } }
instance_variable_set :"##{attrib}", value
or alternatively find the first hash containing the key and fetch its result, falling back to default:
value = [data, template, other_data].find { |h| h.key?(attrib) }.fetch(attrib, default)
Note that this would also allow nil values.

Create an object if one is not found

How do I create an object if one is not found? This is the query I was running:
#event_object = #event_entry.event_objects.find_all_by_plantype('dog')
and I was trying this:
#event_object = EventObject.new unless #event_entry.event_objects.find_all_by_plantype('dog')
but that does not seem to work. I know I'm missing something very simple like normal :( Thanks for any help!!! :)
find_all style methods return an array of matching records. That is an empty array if no matching records are found. And an empty is truthy. Which means:
arr = []
if arr
puts 'arr is considered turthy!' # this line will execute
end
Also, the dynamic finder methods (like find_by_whatever) are officially depreacted So you shouldn't be using them.
You probably want something more like:
#event_object = #event_entry.event_objects.where(plantype: 'dog').first || EventObject.new
But you can also configure the event object better, since you obviously want it to belong to #event_entry.
#event_object = #event_entry.event_objects.where(plantype: 'dog').first
#event_object ||= #event_entry.event_objects.build(plantype: dog)
In this last example, we try to find an existing object by getting an array of matching records and asking for the first item. If there are no items, #event_object will be nil.
Then we use the ||= operator that says "assign the value on the right if this is currently set to a falsy value". And nil is falsy. So if it's nil we can build the object form the association it should belong to. And we can preset it's attributes while we are at it.
Why not use built in query methods like find_or_create_by or find_or_initialize_by
#event_object = #event_entry.event_objects.find_or_create_by(plantype:'dog')
This will find an #event_entry.event_object with plantype = 'dog' if one does not exist it will then create one instead.
find_or_initialize_by is probably more what you want as it will leave #event_object in an unsaved state with just the association and plantype set
#event_object = #event_entry.event_objects.find_or_initialize_by(plantype:'dog')
This assumes you are looking for a single event_object as it will return the first one it finds with plantype = 'dog'. If more than 1 event_object can have the plantype ='dog' within the #event_entry scope then this might not be the best solution but it seems to fit with your description.

How to check if a key exists in a Ruby hash?

I am using search of Net::LDAP, the returned entry is something like this.
#<Net::LDAP::Entry:0x7f47a6491c00
#myhash=
{:loginshell=>["/bin/bash"],
:cn=>["M... R..."],
:homedirectory=>["/mnt/home/m..."],
:uid=>["m..."],
:userpassword=>["{CRYPT}$1$3zR/C...$R1"],
...
}>
I tried to do the following, but failed.
(1)
e = entry.to_hash
e.has_key? "uid"
(2)
entry.has_key? "uid"
The first error says "to_hash" undefined, the second "has_key" undefined. Then I really don't know how to do it, basically I want to find if "uid" is present and if so get its correspondent value.
Thank you very much for the tip.
BTW, it only responds to "entry.uid", but if the search key is provided as a string, how to do that? for example,
def get_value(key)
if entry has key
return key's value
end
end
:uid is a Symbol. That's not a String.
try this:
e.has_key? :uid
The key "uid" doesn't exist. Try
e = Entry.new.myhash
e.has_key?(:uid)
That should return true. If that gives you an error, the problem might lie in your class. Make sure that myhash is defined in the initialize method, and that you use a getter method (or attr_reader) to be able to access the variable. You could use
attr_reader :myhash
right before the initialize method.

how to find a hash key with one of the predefined names?

I have a hash with an arbitrary key:
{'GET': [1,2,3]}
or
{'POST': ['my data 0', 'my data 1']}
The hash is generated from JSON which is sent in the request body. There is just one key, or rather, I ignore any keys but one.
I want to find which key it is, and this is the code that I wrote:
items = data['GET'] || data['get'] || data['POST'] || data['post']
this does not look neat. If the number of keys that I want to process grows the expression will be long. I want it to be short. I am new to Ruby, is there a better way?
If you think it might grow, you may want to separate the HTTP methods from the finding of that method in the data:
methods = [:get, :post]
def find_method(data)
keys = methods.map{|m| [m.to_s.upcase, m.to_s]}.flatten
data.values_at(keys).first
end
You could just get the first value (assuming there's only one) like this:
item = data.values.first
You could use the Hash#values_at method.
http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-values_at
data.values_at('GET','get', 'POST','post').first

How do I elegantly handle nil cases in my array assignment - Ruby?

So I am pushing some elements on my array like this:
upd_city_list << [ j.children[0].text.strip!.gsub(/\s+\W/, ''), j.children[1].text, j.children[1][:href] ]
The above is in an iterator (hence the use of j).
The issue is that from time to time, the j.children[0].text turns up as nil, and Ruby doesn't like that.
I could add a bunch of if statements before this assignment, but that seems a bit inelegant to me.
How do I handle nil cases in this situation in an elegant way?
One possible solution is, when there is a nil value, just push the string none onto the array....but what would that look like?
Thanks.
Edit1:
This is the error I am getting:
NoMethodError: private method ‘gsub’ called for nil:NilClass
The real problem is that strip! returns nil when there are no changes to the string. Your text method is returning a string, it is your strip! method is returning nil. I don't know why it does this. I dislike it, too.
This case of the problem will go away if you just change strip! to strip
In a more general sense, you might create an object to return the array for you. You don't want to go changing (what I assume is) Nokogiri, but you can wrap it in something to hide the train wrecks that result.
You should replace j.children[0].text.strip! with one of two things:
(j.children[0].text || 'none').strip
or
j.children[0].text.to_s.strip
These will, of course, have different effects when the text is nil. I think your ACTUAL problem is that strip! was returning nil, and that should have been obvious to you from the error message.
This might be the case for one to use null object programming pattern. Nil is not a good null object. Try reading here and here. Null object is the elegant way.
nil or a_string will be a_string
so what about (j.children[0].text or 'none')
If you're in rails, this is a great use for the try method.
Also seems that your strip and gsub are redundent. Please consider this implementation:
descriptive_name_1 = j.children[0].text.try(:strip)
descriptive_name_2 = j.children[1].text
descriptive_name_3 = j.children[1][:href]
updated_city_list << [ descriptive_name_1 , descriptive_name_2, descriptive_name_3 ]
w/o try
descriptive_name_1 = j.children[0].text.to_s.strip
descriptive_name_2 = j.children[1].text
descriptive_name_3 = j.children[1][:href]
updated_city_list << [ descriptive_name_1 , descriptive_name_2, descriptive_name_3 ]
If you're in the rails environment you could try try method: https://github.com/rails/rails/blob/82d41c969897cca28bb318f7caf301d520a2fbf3/activesupport/lib/active_support/core_ext/object/try.rb#L50

Resources