`map` based on condition

22,059

Solution 1

Do a select first

clients.select{|c| c.type == 'tablet'}.map(&:ip)

Solution 2

Ruby 2.7+

Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.

For example:

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

Here's a good read on the subject.

Hope that's useful to someone!

Solution 3

Answer is as simple as that:

clients.map { |client| client.ip if client.type == 'tablet' }.compact

Mapping with condition will give nils for clients which failed the condition, for that only we kept compact, which will actually flush the nil values.

Solution 4

An alternate to Sergio Tulentsev's method using #collect. I think using #collect is semantically correct here. I know the O.P. asked how to use #map, but it's my two cents.

clients.collect { |c| c.ip if c.type == "tablet" } # will return nils for clients where the type is not "tablet"

# or

clients.select { |c| c.type == "tablet" }.collect(&ip)
Share:
22,059
sylvian
Author by

sylvian

Updated on June 13, 2020

Comments

  • sylvian
    sylvian about 4 years

    I have a struct like this:

    Struct.new("Test", :loc, :type, :hostname, :ip)
    
    clients = [
    Struct::TestClient.new(1, :pc, "pc1", "192.168.0.1")
    Struct::TestClient.new(1, :pc, "pc2", "192.168.0.2")
    Struct::TestClient.new(1, :tablet, "tablet1", "192.168.0.3")
    Struct::TestClient.new(1, :tablet, "tablet2", "192.168.0.3")
    and etc...
    ]
    

    If I want to get the IP address of all devices, I can use test_clients.map(&:ip). How do I select the IP addresses of specific devices, say all device types called "tablet"? How can I do that with map?

  • silasjmatson
    silasjmatson over 11 years
    Sorry about the duplicate answers, hit the backspace key one too many times and went back to a create rather than edit.
  • PJP
    PJP over 11 years
    I'm curious, how do you differentiate a difference between collect and map? Also, does collect(&ip) return what you expect?
  • silasjmatson
    silasjmatson over 11 years
    I usually use collect when I'm just grabbing values from a set of objects, and map when I'm doing a bunch of logic in the block, or returning an array that has changed values. Am I wrong on that?
  • mu is too short
    mu is too short over 11 years
    The select-less version will leave you with nils.
  • silasjmatson
    silasjmatson over 11 years
    Oh, true. I didn't think about that too much.
  • PJP
    PJP over 11 years
    collect is an alias to map; It doesn't matter which you use because they're identical. Any difference is most likely due to languages you've used in the past because of familiarity with one or the other. And, as a result, I use map because it's easier to type.
  • Sk. Irfan
    Sk. Irfan almost 7 years
    No need of using, select and map as mentioned in the Sergio Tulentsev's answer, as it will result in two loops and comparatively slower.
  • Nick
    Nick over 5 years
    compact also iterates over the array and checks each value for nil. Sergio's answer could actually be faster. In your example, both iterations are over the full length of clients, whereas in Sergio's example, the second iteration may be over a smaller set.
  • theartofbeing
    theartofbeing over 2 years
    This is the correct answer for 2.7 and above