Skip to content

Instantly share code, notes, and snippets.

@cupakromer
Created August 16, 2012 15:23
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save cupakromer/3371003 to your computer and use it in GitHub Desktop.
Save cupakromer/3371003 to your computer and use it in GitHub Desktop.
each_with_object vs inject
my_array = %w[test one two three]
# Inject takes the value of the block and passes it along
# This often causes un-intended errors
my_array.inject({}) do |transformed, word|
puts transformed
transformed[word] = word.capitalize
end
# Output:
# {}
# Test
# IndexError: string not matched
# from (pry):6:in `[]='
# What was really wanted was:
my_array.inject({}) do |transformed, word|
puts transformed
transformed[word] = word.capitalize
transformed
end
# Output:
# {}
# {"test"=>"Test"}
# {"test"=>"Test", "one"=>"One"}
# {"test"=>"Test", "one"=>"One", "two"=>"Two"}
=> {"test"=>"Test", "one"=>"One", "two"=>"Two", "three"=>"Three"}
# On the other hand, each_with_object does exactly as you expect
# It ignores the return value of the block and only passes the
# initial object along
my_array.each_with_object({}) do |word, transformed|
puts transformed
transformed[word] = word.capitalize
"this is ignored"
end
# Output:
# {}
# {"test"=>"Test"}
# {"test"=>"Test", "one"=>"One"}
# {"test"=>"Test", "one"=>"One", "two"=>"Two"}
=> {"test"=>"Test", "one"=>"One", "two"=>"Two", "three"=>"Three"}
Copy link

ghost commented Oct 12, 2015

It works only for creating hash. right?

@sleekweasel
Copy link

Annoying that the order of the parameters is different - I understand 'inject' is historical and 'with_object' is just doing what 'with_index' does - but it's annoying nonetheless.

@cmolenda
Copy link

cmolenda commented Nov 3, 2017

@liznet If you're talking about #inject, it's useful for a lot of things. If you're talking about #each_with_object it's only good for objects with mutable state (which is the thing I was think about when I ran across this thread).

['t', 'l', 'k'].each_with_object('sass'){ |letter, word| word.sub! 's', letter }
# => "talk"

Or an ever so slightly less contrived example

# yes, i realize it's permutations not combinations

class Lock
  def initialize(*combo)
    @combo = combo
    @attempt_values = []
    @locked = true
  end

  def turn_to(value)
    @attempt_values << value
  end

  def locked?
    @combo != @attempt_values
  end
end

combo = [1, 2, 3]

combo.each_with_object(Lock.new(3, 2, 1)){ |value, lock| lock.turn_to value }.locked?
# => true

combo.each_with_object(Lock.new(1, 2, 3)){ |value, lock| lock.turn_to value }.locked?
# => false

My realization was that if I wanted a pure function approach, it's only feasible to use #inject

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment