Reputation: 612
I have this code
notebooks.inject([]) do |res, nb|
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
end
The first nb
has matches the condition and res
looks like this
["xxx1234"]
The second nb
does not match the condition which then delete/clears res
nil
From my understanding, the first value should remain in the array.
I'm also assigning this to a variable and want it to be a one liner.
Upvotes: 0
Views: 1801
Reputation: 2011
inject
works a little differently from how you're imagining. It simply returns the last return value of the loop as it loops through each item. An easy way to fix this is:
notebooks.inject([]) do |res, nb|
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
res # Returns the res array
end
That said, you should probably use select
for your use case as you seem to be just filtering down which set of notebooks you want.. That is:
notebooks.select{|nb| Recipe::NOTEBOOKS.include?(nb.name)}.map(&:guid)
Generally, I've used inject
when I need to run math on a group of items. e.g.
[1,2,3,4].inject(0) {|res, x| x * 2 + res}
Upvotes: 4
Reputation: 13886
If you're open to two loops, but cleaner and still one-liner:
notebooks.select { |nb| Recipe::NOTEBOOKS.include?(nb.name) }.map(&:guid)
Upvotes: 1
Reputation: 121000
The accumulator must be returned on each loop iteration:
notebooks.inject([]) do |res, nb|
Recipe::NOTEBOOKS.include?(nb.name) ? res << nb.guid : res
end
Actually, on each subsequent loop iteration, the accumulator passed to res
block parameter is exactly what was returned from the previous iteration.
In your example, on the second iteration if
returns false
and
res << nb.guid if Recipe::NOTEBOOKS.include?(nb.name)
line is not executed at all. That said, after the second iteration, the accumulator takes a brand new value, that is apparently nil
.
Upvotes: 0