1

i have below code which returns hash value at the end of the method . what i want to do is to return same response without declaring response = {} in the beginning of the method. how to do this in ruby ?

def test 
response = {}
    objects.each do |object|
       response = object.fetch(55)
        break unless response.nil?
    end
   response
end    

5 Answers 5

3

You want to return the first result of fetch(55)? I'd write it down like this

def test
  objects.lazy.map{|o| o.fetch(55)}.reject(&:nil?).first
end
Sign up to request clarification or add additional context in comments.

6 Comments

See @SimpleLime's comment on my answer. It seems that you need o.fetch(55, nil) or o[55].
@engineersmnky: "will still have to traverse the entire enum" - not when it's called on a lazy enumerator.
@engineersmnky: ah, it's a pity you acknowledged your mistake so quickly. I even prepared a script to prove you wrong. What do I do with it now? ¯\_(ツ)_/¯
Are you now working on a script to disprove my comment?
@CarySwoveland: you assume it's a hash. Could very well be api client of some sorts. But for hashes - yes, this form of fetch will not work too well.
|
2

If obtaining a value for each element of the given array (presumably a hash) is cheap, you could write the following.

def test1(arr)
  arr.find { |h| h[55] }&.[](55)
end

else I'd suggest

def test2(arr)
  v = nil 
  arr.find { |h| v = h[55] } && v
end    

even though it doesn't meet your requirement. & in the first is the safe navigation operator, which made its debut in Ruby v2.3. By "cheap" I mean that no extensive calculation is required to determine the value of a key (computing pi to one million digits, for example). See Hash#[].

Suppose

a1 = [{ 9=>'cat', 14=>'dog', 12=>'pig' }, { 9=>'cat', 55=>'dog', 12=>'pig' }]
a2 = [{ 9=>'cat', 14=>'dog', 12=>'pig' }, { 9=>'cat', 12=>'dog', 12=>'pig' }]

then

test1(a1)
  #=> 'dog'
test1(a2)
  #=> nil

test2(a1)
  #=> 'dog'
test2(a2)
  #=> nil

As pointed out in a comment, the following is a variant of test1.

def test1(arr)
  arr.find(->{{}}) { |h| h[55] }[55]
end

See Enumerable#find, specifically, if no match is found and find has an argument which responds to call (namely, a proc or method), that argument is called and the result is returned by find. (To use a method rather than a proc: def m() {} end; arr.find(method(:m)) { |h| h[55] }[55].)

4 Comments

Safe navigation is fun and all but why not just use arr.find (->{{}}) { |h| h[55] }[55]?
@engineersmnky, I just noticed your comment. I believe you are suggesting (arr.find ->{{}} { |h| h[55] })[55], which works, but I don't understand it. Please explain.
Sorry It should have been arr.find(->{{}}) { |h| h[55] }[55]. Enumerable#find accepts an ifnone callable object to be called in the event no matching element is found, so in this case we are just passing a lambda that returns an empty Hash. Then we use Hash#[] to act on the empty hash or the hash returned by the find block.
@engineersmnky, I wasn't aware that find takes an optional argument (and has done so since at least v1.9). Good to know. I updated my answer. Thanks.
1

Why not return from the block?

def test 
  objects.find do |object|
     response = object.fetch(55)
     return response if response
  end
end

Note that this only works in a method. You get a LocalJumpError if you try this in irb, for example.

If you just want to get the value out of the block and not actually return from the method, use break:

def test 
  response = objects.find do |object|
     response = object.fetch(55)
     break response if response
  end
end

2 Comments

@engineersmnky That would return the object not the response.
But, as Cary pointed out, each returns objects if nothing is found, but find returns nil, which is nicer.
0

Ruby automatically returns the value that is placed in the last line so you can use that logicc

1 Comment

objects.each {...} returns objects (each always returns its receiver), so removing the line response would cause objects to be returned.
0

If you're just looking for the first instance in the collection that matches a conditional, ruby has an idiomatic solution: #detect

object = [{1 => true}, {2 => true}, {55 => 'foo'}, {55 => 'bar'}]
object.detect { |o| o.fetch(55) { false } } #=> {55=>"foo"}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.