Ruby conditional map and replace

4.3k Views Asked by At

I'm trying to conditionally map an array I have:

Array that looks like [ "a", "b", "c" ]

I want to map it inline (using map!) to change anything that matches /b/ and change it to "bb". This doesn't seem to work though, it doesn't seem to be changing the array.

I want it to look like this [ "a", "bb", "c" ]

define the array:

irb(main):001:0> a = [ "a", "b", "c" ]
=> ["a", "b", "c"]

Try with a select chained to a map!, and it shows the changed value for the one element, and it didn't change the original array.

irb(main):002:0> a.select { |e| e =~ /b/ }.map! { |e| e = "bb" }
=> ["bb"]
irb(main):003:0> p a.inspect
"[\"a\", \"b\", \"c\"]"

So I tried doing the same thing, but assigning it to a new array, this only creates an array with any changed elements - not what I want

irb(main):004:0> cc = a.select { |e| e =~ /b/ }.map! { |e| e = "bb" } 
=> ["bb"]
irb(main):005:0> p cc.inspect
"[\"bb\"]"
=> nil

What am I missing here? I'm missing some Ruby fundamental knowledge here I think.

2

There are 2 best solutions below

0
On

You call select on a, which returns new instance of Array, on which in the next step you call map!, modifying this new instance (not the original one). To achieve what you want, you can do:

a.map! { |e| e =~ /b/ ? 'bb' : e }
2
On

The correct logic looks like this:

results = a.map { |e| if e =~ /b/ then "bb" else e end }

The same logic using the ternary operator:

results = a.map { |e| e =~ /b/ ? "bb" : e }

If you really need to do the replace in-place then use ! like this:

a.map! { |e| e =~ /b/ ? "bb" : e }

Here's why your code in your post isn't working the way you expect:

  • Your select is returning a new array, then map! is modifying the new array. That's one issue.

  • More important, the select method doesn't work the way you're using it. The select method returns only the items that match.

In general, don't use ! for map unless you have a really good reason, and you understand it better, because Ruby ! methods that modify objects in place tend to be a bit harder to debug and test reliably.