erikamaker / pickaxe

Study notes and exercises from Programming Ruby 3.2
0 stars 0 forks source link

Ch 4 — map vs each #8

Closed ianterrell closed 1 year ago

ianterrell commented 1 year ago

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 see map you know they want to be transformed into something new for subsequent use.

erikamaker commented 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?

ianterrell commented 1 year ago

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.

ianterrell commented 1 year ago

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]
erikamaker commented 1 year ago

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 😄