简体   繁体   中英

Iterate over just some elements of a Hash in Ruby

I've got a Hash that looks like this:

card = {
  name: "Mrs.Jones",
  number: "4242 4242 4242 4242",
  exp_month: "12",
  exp_year: "2014",
  address: "90210 Beverly Hills",
  added: "2014-11-09 09:14:23"
}

I'd like to iterate over just the number , exp_month and exp_year fields and update them. What's the most Ruby-like way of doing that?

This is what my code looks like at present:

card.each do |key,value|
  card[key] = encrypt(value) # Only apply to number, exp_month and exp_year
end

I'd do it the following way:

ENCRYPTED_FIELDS = [:number, :exp_month, :exp_year]

card.each do |key,value|
  card[key] = encrypt(value) if ENCRYPTED_FIELDS.include?(key)
end

But a better option would be to make a class for CreditCardDetails and define the setters to encrypt the data:

class CreditCardDetails
  def initialize(hash)
    hash.each do |k, v|
      self.send("#{k}=", v)
    end
  end

  #example for not encrypted field
  def name=(value)
    @name = value
  end 

  #example for encrypted field
  def number=(value)
    @number = encrypted(value)
  end
end

Since you already know which keys you'd like to encrypt, you can iterate over the wanted key names, instead of the hash:

ENCRYPTED_FIELDS = [:number, :exp_month, :exp_year]

ENCRYPTED_FIELDS.each do |key|
  card[key] = encrypt(card[key])
end

Here are a couple of ways to do it. I've replaced the method encrypt with size , for purposes of illustration. They both use the form of Hash#merge that takes a block. The second approach does not use keys. Instead, it processes the value if the value is all digits (and spaces). I included that mainly to illustrate what you might do in other applications.

#1

card.merge(card) do |k,_,v|
  case k
  when :number, :exp_month, :exp_year
    v.size
  else
    v
  end
end
  #=> {:name=>"Mrs.Jones", :number=>19, :exp_month=>2, :exp_year=>4,
  #    :address=>"90210 Beverly Hills", :added=>"2014-11-09 09:14:23"}

#2

card.merge(card) { |*_,v| v[/^[\s\d]+$/] ? v.size : v }
  #=> {:name=>"Mrs.Jones", :number=>19, :exp_month=>2, :exp_year=>4,
  #    :address=>"90210 Beverly Hills", :added=>"2014-11-09 09:14:23"}

If you want to mutate card , use Hash#update (aka merge! ) rather than merge :

#1a

card.update(card) do |k,_,v|
  case k
  when :number, :exp_month, :exp_year
    v.size
  else
    v
  end
end
  #=> {:name=>"Mrs.Jones", :number=>19, :exp_month=>2, :exp_year=>4,
  #    :address=>"90210 Beverly Hills", :added=>"2014-11-09 09:14:23"}

card
  #=> {:name=>"Mrs.Jones", :number=>19, :exp_month=>2, :exp_year=>4,
  #    :address=>"90210 Beverly Hills", :added=>"2014-11-09 09:14:23"}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM