James Klein
James Klein

Reputation: 612

Ruby inject to create array

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

Answers (3)

Julie
Julie

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

Pedro Nascimento
Pedro Nascimento

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

Aleksei Matiushkin
Aleksei Matiushkin

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

Related Questions