Closed ianterrell closed 1 year ago
I appreciate it! But, I am always glad for your input. It's helping me level up faster than I could on my own. I'm going to run this back at you if that's okay, because it still doesn't feel totally intuitive to me.
So, for a simple each
block, the elements have a block executed on it, and the value is returned. With map
, a new collection is created (using each element that had been run through a block).
Though neither of these methods actually mutate the original collection, they both perform a block on its elements. It's just how that return value is stored differs. Is that right?
So, for a simple each block, the elements have a block executed on it, and the value is returned.
If by the value you mean the original collection; yes. Back to our old friend object_id
...
x # => [1, 2]
x.object_id # => 59840
x.each{|a|a} # => [1, 2]
x.each{|a|a}.object_id # => 59840
x.each{|a|a}.object_id # => 59840
x.each{|a|a}.object_id # => 59840
x.map{|a|a} # => [1, 2]
x.map{|a|a}.object_id # => 252580
x.map{|a|a}.object_id # => 303900
x.map{|a|a}.object_id # => 309080
With each
you're not creating a new collection; with map
you are.
But yep — as always, you've got it.
As I couldn't sleep last night I realized how terrible an example that was using a block that returned the same item; the return values look the same that way. I'm sure it was obvious, and the object ids show it also, but this shows much more clearly that each "does something and returns the same collection" vs map which "transforms values into a new collection of mapped values".
x # => [1, 2]
x.each { |item| Time.now } # => [1, 2]
x.map { |item| Time.now } # => [2023-03-29 08:51:45.215583 -0400, 2023-03-29 08:51:45.215584 -0400]
That makes sense! I didn't get a whole lot done in the Ruby text today (yet), but I got past each
, and map
, up to tap
. With tap
, we'd be looking at potentially mutating the same object, like this example I threw together:
irb(main):001:0> person = { name: 'Georgia', wage: 15 }
=> {:name=>"Georgia", :wage=>15}
irb(main):002:1* def give_raise(employee)
irb(main):003:2* employee.tap do |item|
irb(main):004:2* item[:wage] += 1
irb(main):005:1* end
irb(main):006:0> end
=> :give_raise
irb(main):007:0> give_raise(person)
=> {:name=>"Georgia", :wage=>16}
I'm very stoked that these three concepts are coming together for me today. Some parts of this chapter are review, but this is an example of something brand new I couldn't have explained before. Unless I don't have something right, I'm going to mark this closed for now 😄
Super brief — and please tell me if I start to annoy or this becomes unhelpful —
The sort of folk wisdom around each vs map is:
Generally if you don't want to keep the results around you don't want to use map — that will allocate new arrays or collections and increase memory usage, etc. If you just want to do something with them, use each. But if you want the results for later (or a subsequent step), then you want map.
Often the memory usage doesn't really matter concretely, and then it comes down to semantics and how the code communicates the programmer's intent. If you see
each
you can stop thinking about those values when it's done; if you seemap
you know they want to be transformed into something new for subsequent use.