55502f40dc8b7c769880b10874abc9d0

I'm looking for a better way to do this using a block structure and the regex backreference inside the block (unused here). I'm not really fond of the dual sub().sub() I used to get at the pertinent bit of the hash key.

Given a hash with some keys in the form "svc_XX_id", where XX is a two letter code, extract an array with the form ["XX.<value>", "XX.<value>"].

# Initial attributes Hash
# 
attributes = {
  "recs"=>nil, 
  "location"=>nil,
  "uname"=>"b", 
  "svc_tw_id"=>99991212, 
  "lang"=>nil,
  "hash"=>"8421ce0c2d9eabba44b9c5bvhr8u672ecc", 
  "id"=>33, 
  "has_p"=>nil, 
  "full_name"=>"Ben Roos", 
  "time_zone"=>nil, 
  "referrals"=>nil, 
  "bio"=>nil, 
  "svc_fb_id"=>12129999, 
  "email"=>"email@domain.tld"
}

# Array I need to extract from the hash:
#
ary_end_result = ["fb.12129999", "tw.99991212"]


# My newbie solution:
# Greps for matching keys, then collects them by removing "svc_" and "_id" and setting value to key of hash
# Would it be more elegant/efficient to use a regex object inside a block?
#
ary_end_result = attributes.keys.grep(/^svc_(\w{2})_id$/).collect do |key|      
  key.sub("svc_", "").sub("_id", "") + ".#{attributes[key]}"
end

Refactorings

No refactoring yet !

947f9ac32c0342a5630a1da2fdf9a47b

Daren

February 24, 2010, February 24, 2010 23:58, permalink

No rating. Login to rate!

use captures, extend the regular expression to include in the first capture the period and sequence of digits.

attributes.keys.each do |key|
  if key =~ /^svc_(\w{2}\.\d+)_id$/
    puts #{$1}
  end
end
6443bb93887587957722465397211008

Brian

February 25, 2010, February 25, 2010 01:18, permalink

No rating. Login to rate!

Using the above from Daren as a guide, the new version is:

ary_svc_ids = []
attributes.keys.each do |key|
  if key =~ /^svc_(\w{2})_id$/
    ary_end_result[ary_end_result.length] = "#$1." + attributes[key].to_s
  end
end
6443bb93887587957722465397211008

Brian

February 25, 2010, February 25, 2010 01:21, permalink

No rating. Login to rate!

Oops, naming of initial array was wrong. Still wishing I could find a version that used .collect instead of .each and didn't fill the resulting array with nil when the regex doesn't match.

ary_end_result = []
attributes.keys.each do |key|
  if key =~ /^svc_(\w{2})_id$/
    ary_end_result[ary_end_result.length] = "#$1." + attributes[key].to_s
  end
end
C7773cc658b976a0de5eed4d257ff5c5

Clayton Lengel-Zigich

February 25, 2010, February 25, 2010 02:47, permalink

1 rating. Login to rate!

Just use .compact on your returned array to get rid of the nils. I've added it to your example, although I haven't tested it.

ary_end_result = []
attributes.keys.map do |key|
  if key =~ /^svc_(\w{2})_id$/
    ary_end_result[ary_end_result.length] = "#$1." + attributes[key].to_s
  end
end.compact
A74c34f1043b833b1fcc86ce9f3521ee

Pavel Gorbokon

February 25, 2010, February 25, 2010 10:53, permalink

No rating. Login to rate!
ary_end_result = attributes.inject([]) {|array, (key, value)| 
  key =~ /^svc_(\w{2})_id$/ ? array << "#{$1}.#{value}" : array
}
25ff3dfe48d3847ecf9971aab99589fb

mxcl

February 25, 2010, February 25, 2010 13:42, permalink

No rating. Login to rate!

This is probably the most succinct, combining features of all the above efforts.

attributes.collect{ |(key, value)| "#{$1}.#{value}" if key =~ /^svc_(\w\w)_id$/ }.compact
E8f0d6e9bc8bca695b3c5bdf75cdcc03

Martin Plöger

March 8, 2010, March 08, 2010 20:53, permalink

No rating. Login to rate!

@mxcl: Seems to be the most succinct and readable solution... except: you can get rid of the braces around the block-arguments. Since #collect on a hash returns two args they will be assigned correcly. And call #map (which is shorter ;-)):

attributes.map { |key, value| "#{$1}.#{value}" if key =~ /^svc_(\w{2})_id$/ }.compact
22a20f53880a5555e9f0e6aa2f499d5e

Bogdan Kulbida

March 25, 2011, March 25, 2011 10:00, permalink

1 rating. Login to rate!

Try this out.

dest = raw.select {|k,v| v=~/^svc_(\w{2})_id$/}

Your refactoring





Format Copy from initial code

or Cancel