# 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 !
Daren
February 24, 2010, February 24, 2010 23:58, permalink
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
Brian
February 25, 2010, February 25, 2010 01:18, permalink
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
Brian
February 25, 2010, February 25, 2010 01:21, permalink
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
Clayton Lengel-Zigich
February 25, 2010, February 25, 2010 02:47, permalink
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
Pavel Gorbokon
February 25, 2010, February 25, 2010 10:53, permalink
ary_end_result = attributes.inject([]) {|array, (key, value)|
key =~ /^svc_(\w{2})_id$/ ? array << "#{$1}.#{value}" : array
}
mxcl
February 25, 2010, February 25, 2010 13:42, permalink
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
Martin Plöger
March 8, 2010, March 08, 2010 20:53, permalink
@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
Bogdan Kulbida
March 25, 2011, March 25, 2011 10:00, permalink
Try this out.
dest = raw.select {|k,v| v=~/^svc_(\w{2})_id$/}
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>"].