Warm tip: This article is reproduced from serverfault.com, please click

How to optimize mapping hash that contains similar keys and values?

发布于 2020-11-28 08:55:37

I've got some constants defined like this

CONSUMER_TYPE = 'consumer'
CONSUMER_1_TYPE = "#{CONSUMER_TYPE}1"
CONSUMER_2_TYPE = "#{CONSUMER_TYPE}2"
CONSUMER_3_TYPE = "#{CONSUMER_TYPE}3"

INDUSTRIAL_TYPE = 'industrial'
INDUSTRIAL_1_TYPE = "#{INDUSTRIAL_TYPE}1"
INDUSTRIAL_2_TYPE = "#{INDUSTRIAL_TYPE}2"
INDUSTRIAL_3_TYPE = "#{INDUSTRIAL_TYPE}3"

SERVICES_TYPE = 'services'
SERVICES_1_TYPE = "#{SERVICES_TYPE}1"
SERVICES_2_TYPE = "#{SERVICES_TYPE}2"
SERVICES_3_TYPE = "#{SERVICES_TYPE}3"

The record field can have values like services2 or industrial1. In my model I've created a mapping method that's supposed to return hash with different set of attributes depending on the record field value like so

def classification_attributes
  product_type_mapping[product_type]
end

def product_type_mapping
  {
     CONSUMER_1_TYPE => { abc: abc, vpn: vpn, lbc: lbc },
     CONSUMER_2_TYPE => { abc: abc, vpn: vpn, lbc: lbc },
     CONSUMER_3_TYPE => { abc: abc, vpn: vpn, lbc: lbc },
     INDUSTRIAL_1_TYPE => { vpn: vpn, htt: htt, bnn: bnn },
     INDUSTRIAL_2_TYPE => { vpn: vpn, htt: htt, bnn: bnn },
     INDUSTRIAL_3_TYPE => { vpn: vpn, htt: htt, bnn: bnn },
     SERVICES_1_TYPE => { dhy: dhy, rtt: rtt, abc: abc },
     SERVICES_2_TYPE => { dhy: dhy, rtt: rtt, abc: abc },
     SERVICES_3_TYPE => { dhy: dhy, rtt: rtt, abc: abc }
  }
end

For instance, if a record contains a value consumer3, the mapping method should return { abc: abc, vpn: vpn, lbc: lbc }. As you can see there's a lot of code duplication. I was wondering if there might be more optimal and concise way of tackling this task.

Questioner
Alex Shmatko
Viewed
0
Schwern 2020-11-28 17:37:40
  1. Use Symbols instead of constants.
  2. Don't expose the mapping.

Constants in Ruby are mostly about information hiding. For example, if the key changes from consumer1 to consumer_1 as long as everything accesses the Hash with CONSUMER_1_TYPE you're ok. Why risk it?

Instead, fully hide the Hash. Now that it's hidden, constants are not necessary. Use Symbols.

If all the values are going to be the same, put them into their own methods.

def classification_attributes(product_type)
  product_type_mapping[product_type]
end

private def consumer_config
  { abc: abc, vpn: vpn, lbc: lbc }
end

private def industrial_config
  { vpn: vpn, htt: htt, bnn: bnn }
end

private def services_config
  { dhy: dhy, rtt: rtt, abc: abc }
end

private def product_type_mapping
  {
     conumser1: consumer_config,
     consumer2: consumer_config,
     consumer3: consumer_config,
     industrial1: industrial_config,
     industrial2: industrial_config,
     industrial3: industrial_config,
     services1: services_config,
     services2: services_config,
     services3: services_config
  }
end

That's about as far as I can say without more context. If there's that much redundancy you may be able to split product_type into type and subtype.


Consider moving product_type_mapping into config/application.rb, plus any other related configurations. This keeps the application configuration in one place, not scattered around in various classes.

module YourApp
  class Application < Rails::Application
    config.x.consumer_config = { abc: abc, vpn: vpn, lbc: lbc }.freeze
    config.x.industrial_config = { vpn: vpn, htt: htt, bnn: bnn }.freeze
    config.x.services_config = { dhy: dhy, rtt: rtt, abc: abc }.freeze

    config.x.product_type_mapping = {
      conumser1: config.x.consumer_config,
      consumer2: config.x.consumer_config,
      consumer3: config.x.consumer_config,
      industrial1: config.x.industrial_config,
      industrial2: config.x.industrial_config,
      industrial3: config.x.industrial_config,
      services1: config.x.services_config,
      services2: config.x.services_config,
      services3: config.x.services_config
    }.freeze
  end
end

# in your class...

def classification_attributes(product_type)
  Rails.configuration.x.product_type_mapping[product_type]
end