Any suggestions for refactoring this ugly case-switch into something more elegant?
This method (in Ruby) returns a (short or full) description for Belgian provinces, given a zipcode.
def province(zipcode, short = false)
case zipcode
when 1000...1300
short ? 'BXL' : 'Brussel'
when 1300...1500
short ? 'WBR' : 'Waals-Brabant'
when 1500...2000, 3000...3500
short ? 'VBR' : 'Vlaams-Brabant'
when 2000...3000
short ? 'ANT' : 'Antwerpen'
when 3500...4000
short ? 'LIM' : 'Limburg'
when 4000...5000
short ? 'LIE' : 'Luik'
when 5000...6000
short ? 'NAM' : 'Namen'
when 6000...6600, 7000...8000
short ? 'HAI' : 'Henegouwen'
when 6600...7000
short ? 'LUX' : 'Luxemburg'
when 8000...9000
short ? 'WVL' : 'West-Vlaanderen'
when 9000..9999
short ? 'OVL' : 'Oost-Vlaanderen'
else
fail ArgumentError, 'Not a valid zipcode'
end
end
Based on suggestions from MiiinimalLogic i made a second version. It this preferable?
class Provincie
ProvincieNaam = Struct.new(:kort, :lang)
PROVINCIES = {
1000...1300 => ProvincieNaam.new('BXL', 'Brussel'),
1300...1500 => ProvincieNaam.new('WBR', 'Waals-Brabant'),
1500...2000 => ProvincieNaam.new('VBR', 'Vlaams-Brabant'),
2000...3000 => ProvincieNaam.new('ANT', 'Antwerpen'),
3000...3500 => ProvincieNaam.new('VBR', 'Vlaams-Brabant'),
3500...4000 => ProvincieNaam.new('LIM', 'Limburg'),
4000...5000 => ProvincieNaam.new('LIE', 'Luik'),
5000...6000 => ProvincieNaam.new('NAM', 'Namen'),
6000...6600 => ProvincieNaam.new('HAI', 'Henegouwen'),
6600...7000 => ProvincieNaam.new('LUX', 'Luxemburg'),
7000...8000 => ProvincieNaam.new('HAI', 'Henegouwen'),
8000...9000 => ProvincieNaam.new('WVL', 'West-Vlaanderen'),
9000..9999 => ProvincieNaam.new('OVL', 'Oost-Vlaanderen')
}.freeze
def self.lang(postcode)
provincie_naam(postcode).lang
end
def self.kort(postcode)
provincie_naam(postcode).kort
end
def self.provincie_naam(postcode)
PROVINCIES.each { |list, prov| return prov if list.cover?(postcode) }
fail ArgumentError, 'Geen geldige postcode'
end
private_class_method :provincie_naam
end
Personally, I'd specify the zip ranges & Province information in a different data structure a la Map of Range objects/Provinces, then use Ruby's Range methods to check if the result falls in the range with the range's method.
You could consider having just one range lookup, either as is done here or with a map structure, with a secondary lookup (probably in a map) from the short description to the long description.
Related
Since the release of Ruby 2.5 we have been given the new #yield_self method. I think this is a fantastic addition as finally I can string large active record scopes (conditionally) together.
However, while reading this page it has the following example:
Event.upcoming
.yield_self { |events| params[:limit] ? events.limit(params[:limit]) : events }
.yield_self { |events| params[:status] ? events.where(status: params[:status]) : events }
# Or even
Event.upcoming
.yield_self { |_| params[:limit] ? _.limit(params[:limit]) : _ }
.yield_self { |_| params[:status] ? _.where(status: params[:status]) : _ }
# Or even
def with_limit(events)
params[:limit] ? events.limit(params[:limit]) : events
end
def with_status(events)
params[:status] ? events.where(status: params[:status]) : events
end
Event.upcoming
.yield_self(&method(:with_limit))
.yield_self(&method(:with_status))
Which is exactly what I want to do, however I was wondering, regarding the bottom bit, could you write this instead?
Event.upcoming
.yield_self(&:with_limit)
.yield_self(&:with_status)
Or do you specifically need the &method part to call the methods properly with yield_self?
I have the following Ruby hash:
{"limit"=>250, "days_ago"=>14, "days_ago_filter"=>"lt", "key"=>3}
I'd like to convert it to a human-readable string and translate some of the values as necessary:
Limit: 250 - Days Ago: 14 - Days Ago Filter: Less than - Key: D♯, E♭,
So lt, in this case, actually translates to Less than. and 3 for key translates to D♯, E♭.
I'm almost there with this:
variables.map {|k,v| "#{k.split('_').map(&:capitalize).join(' ')}: #{v}"}.join(' - ')
But translating those values is where I'm hitting a snag.
I'd suggest using hashes for mapping out the possible values, e.g.:
days_ago_filter_map = {
"lt" => "Less than",
# ...other cases here...
}
musical_key_map = {
3 => "D♯, E♭",
# ...other cases here...
}
Then you can switch on the key:
variables.map do |key, value|
label = "#{key.split('_').map(&:capitalize).join(' ')}"
formatted_value = case key
when "days_ago_filter" then days_ago_filter_map.fetch(value)
when "key" then musical_key_map.fetch(value)
else value
end
"#{label}: #{formatted_value}"
end.join(' - ')
Note that if you're missing anything in your maps, the above code will raise KeyNotFound errors. You can set a default in your fetch, e.g.
days_ago_filter_map.fetch(value, "Unknown filter")
musical_key_map.fetch(value, "No notes found for that key")
You can create YAML files too for such kind of mappings :
values_for_replacement = {
"lt" => "Less than",
3 => "D♯, E♭"
}
you can try following :
variables.map {|k,v|
value_to_be_replaced = values_for_replacement[v]
"#{k.humanize}: #{(value_to_be_replaced.present? ? value_to_be_replaced : v)}"}.join(' - ')
I have a code which looks like this
require 'net/http'
base = 'www.uniprot.org'
tool = 'mapping'
params = {
'from' => 'ACC+ID', 'to' => 'P_ENTREZGENEID', 'format' => 'tab',
'query' => 'A0A0K3AVS5 A0A0K3AVV4 A0A0K3AW32 A0A0K3AWP0'
}
http = Net::HTTP.new base
$stderr.puts "Submitting...\n";
response = http.request_post '/' + tool + '/',
params.keys.map {|key| key + '=' + params[key]}.join('&')
loc = nil
while response.code == '302'
loc = response['Location']
response = http.request_get loc
end
while loc
wait = response['Retry-After'] or break
$stderr.puts "Waiting (#{wait})...\n";
sleep wait.to_i
response = http.request_get loc
end
response.value # raises http error if not 2xx
puts response.body
which gives me what I need. however, I have two questions
1- How to load a query list instead I parse it into the code ? lets say I save a txt file with all the query i want to the desktop of a mac
2- How to export the output ?
If I have
B2D6P1
G5EC52
B2FDA8-2
B2MZB1
B3CJ34
B3CKG1
B3GWA1
what #tadman showed gives me the answer
however, I have the following
B2D6P1
G5EC52;B2D6P4
B2FDA8-2;B2FDA8
B2MZB1;P18834
B3CJ34
B3CKG1
B3GWA1;Q8I7K5
and the answer is like below
B2D6P1 rmd-2
G5EC52 tlf-1
B2D6P4 tlf-1
B2FDA8 smc-3
B2MZB1 col-14
P18834 col-14
B3CJ34 gcn-1
B3CKG1 urm-1
B3GWA1 nono-1
Q8I7K5 nono-1
what I want is that if I have two entries in each row (separated with ;) leading to the similar output , it gives me only one , otherwise give me as many as they have for example in above example , my desire output is
B2D6P1 rmd-2
G5EC52;B2D6P4 tlf-1
B2FDA8-2;B2FDA8 smc-3
B2MZB1;P18834 col-14
B3CJ34 gcn-1
B3CKG1 urm-1
B3GWA1;Q8I7K5 nono-1
is this possible ?
Reading query data:
query = File.readlines('ids.txt').map(&:chomp).join(' ')
That way you can have them on separate lines, easier to edit, and they're space separated when submitted.
That makes your params look like:
params = {
'query' => query,
...
}
Writing data:
File.open('output.txt', 'w') do |f|
f.write(response.body)
end
That's all there is to it. If it's a string, or can be converted to a string, you can write it to a file.
I'm looking at some code containing the following line:
a <= to_sfixed (-3.125, 7, -6);
What is in a after this line has been run?
I think I've found the function in float_pkg_c.vhd, this appears to be a key part of the code:
result_big := to_sfixed (
arg => STD_LOGIC_VECTOR(rsigned),
left_index => left_index,
right_index => (right_index-3));
result := resize (arg => result_big,
left_index => left_index,
right_index => right_index,
round_style => round_style,
overflow_style => overflow_style);
I've read the guidance. From here:
To_sfixed – “float” to “sfixed”. Inputs: arg (float), left_index and
right_index (natural) OR size_res (ufixed). Parameters round_style :
Boolean (true), overflow_style : Boolean (true), check_error : Boolean
(true), and denormalize : Boolean (true).
However, I still haven't worked out the expected bits - Am I right to be expecting 13bits in total?
Is there a simple way of answering such questions (e.g. with a command line interface) for myself?
I have my #albums which are working fine; pickung data up with albums.json.
What I'd like to do is to split this #albums in three parts.
I was thinking of something like { "ownAlbums" : [ ... ], "friendSubscriptions" : [ ... ], "otherSubscriptions" : [ ... ] } But I got several errors like
syntax error, unexpected tASSOC, expecting kEND #albums["own"] => #albums
when I tried
#albums["own"] => #albums
or
TypeError (Symbol as array index):
when I tried:
#albums[:otherSubscriptions] = 'others'
and so on.
I never tried something like this before but this .json is just a simple array ?
How can I split it in three parts ?
Or do I have to modify the active record to do so ? Because if so, I'd find another way than splitting.
Second Edit
I tried something like this and it's actually working:
#albums = [*#albums]
own = []
cnt = 0
#albums.each do |ownAlbum|
cnt = cnt.to_int
own[cnt] = ownAlbum
cnt=cnt+1
end
subs = Subscription.where(:user_id => #user.user_id)
#albums[0] = own
#albums[1] = subs
But where I have [0] and [1] I'D prefer Strings.
But then I get the error: TypeError (can't convert String into Integer):
How to get around that ?