I use an array to store fieldvalues. To easily add and access elements I use constants as element identifiers.
Until now I did this by hand like this:
stages = ["lidnummer","aardf","prest","dt_prest","aantal","bedrag","verstrekker","foutcode"]
values = ["it","can","be","anything",0,5.3,nil,88]
LIDNUMMER,AARDF,PREST,DT_PREST,AANTAL,BEDRAG,VERSTREKKER,FOUTCODE = 0,1,2,3,1,5,6,7
p values[AARDF] => "can"
Now I have automated this like:
stages = ["lidnummer","aardf","prest","dt_prest","aantal","bedrag","verstrekker","foutcode"]
values = ["it","can","be","anything",0,5.3,nil,88]
stages.each do |c|
eval "#{c.upcase} = #{stages.index(c)}"
end
p values[AARDF] => "can"
But I suppose there is a better Ruby-way to do this, and perhaps without the eval, are there suggestions?
This the easiest I can think of if you insist on doing it the way you described and not using hashes:
stages.zip(values) { |stage, value| Object.const_set(stage.upcase, value) }
values[BEDRAG] #=> 5
Personally I'd just build a hash, but you know your requirements better than I do:
Hash[stages.map(&:upcase).zip(values)]
#=> {"LIDNUMMER"=>0, "AARDF"=>1, "PREST"=>2, "DT_PREST"=>3, "AANTAL"=>4, "BEDRAG"=>5, "VERSTREKKER"=>6, "FOUTCODE"=>7}
Yes, use a Hash and Symbols.
stages = {:aardf => "aardf", :prest => "prest", ...}
stages[:aardf]
For legacy, use
AARDF = :aardf
and
stages[AARDF]
will still work.
other way you may get interested -
stages = ["lidnummer","aardf","prest","dt_prest","aantal","bedrag","verstrekker","foutcode"]
values = [0,1,2,3,4,5,6,7]
stages = stages.zip(values).inject({}) {|h, (k,v)| h[k.upcase]=v;h}
=> {"LIDNUMMER"=>0, "FOUTCODE"=>7, "BEDRAG"=>5, "VERSTREKKER"=>6, "AANTAL"=>4, "PREST"=>2, "AARDF"=>1, "DT_PREST"=>3}
p stages['AARDF'] #=> 1
Thanks all, i learned a lot of this.
Michael was the nearest with his answer but not right (the values array can contain anything, normally it is not the order of the element).
I managed to combine the different techniques here presented and came up with the following.
stages = ["lidnummer","aardf","prest","dt_prest","aantal","bedrag","verstrekker","foutcode"]
values = ["it","can","be","anything",0,5.3,nil,88]
stages.each_with_index{|c,i|Object::const_set(c.upcase, i)}
p values[AARDF] => "it"
p values[BEDRAG] => 5.3
If i use a Hash it is more error sensitive and i would end using
p values[hash["AARDF"]]
which is not as smooth
Related
I want to remove a pair of 'duplicates' from an array of strings, where each element has the form R1,R2, with varying numbers. In my case, a duplicate would be R2,R1 because it has the same elements of R1,R2 but inverted.
Given:
a = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
The resulting array should be like so:
a = ['R1,R2', 'R3,R4', 'R5,R6']
How could I remove the duplicates so I would have the following?
A solution with Set
require 'set'
a.uniq { |item| Set.new(item.split(",")) } # => ["R1,R2", "R3,R4", "R5,R6"]
Here is a working example :
array = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
array.uniq { |a| a.split(',').sort }
try this,
def unique(array)
pure = Array.new
for i in array
flag = false
for j in pure
flag = true if (j.split(",").sort == i.split(",").sort)
end
pure << i unless flag
end
return pure
end
reference: https://www.rosettacode.org/wiki/Remove_duplicate_elements#Ruby
If the elements of your array are "pairs", they should maybe be actual pairs and not strings, like this:
pairs = [['R1', 'R2'], ['R3', 'R4'], ['R2', 'R1'], ['R5', 'R6']]
And, in fact, since order doesn't seem to matter, it looks like they really should be sets:
require 'set'
sets = [Set['R1', 'R2'], Set['R3', 'R4'], Set['R2', 'R1'], Set['R5', 'R6']]
If that is the case, then Array#uniq will simply work as expected:
sets.uniq
#=> [#<Set: {"R1", "R2"}>, #<Set: {"R3", "R4"}>, #<Set: {"R5", "R6"}>]
So, the best way would be to change the code that produces this value to return an array of two-element sets.
If that is not possible, then you should transform the value at your system boundary when it enters the system, something like this:
sets = a.map {|el| el.split(',') }.map(&Set.method(:new))
I'm looking for a Ruby in-memory key-value store that allows me to select all values for the keys that match a regular expression.
I have looked online but I can't find something the does what I need.
Any suggestion?
Thanks
Update
After reviewing my question I noticed I wasn't probably very clear so, also because someone of you guys asked got it, here an example.
I'm currently using Daybreak in my app so I use it for the example but it does not work.
require 'daybreak'
db = Daybreak::DB.new "example.db"
db['prefix_foo'] = 'first'
db['prefix_bar'] = 'second'
db['doo'] = 'third'
db.magic('prefix') #=> [ db['prefix_foo'], db['prefix_bar']]
Or
db.magic('prefix') #=> ['prefix_foo', 'prefix_bar']
I'd rather not to use a "naked" hash.
Thanks again for your help.
str = {:make => "bmw", :year => "2003"}
str.select{|k,v| k =~ /ak/}
#=> {:make=>"bmw"}
str.select{|k,v| k =~ /ak/}.values #=> get only values
#=> ["bmw"]
Hi i am little struggle to make this hash and sort it by created of and key , value pair.
Here is my code
hash_answers = {}
unless answers.blank?
answers.each_with_index do |ans ,index|
voted_up_users = ans.votes_up_by_all_users(ans)
voted_down_users = ans.votes_down_by_all_users(ans)
hash_answers[ans.id] = voted_up_users.count -voted_down_users.count #line one
hash_answers[index+1] = ans.created_at # line 2
end
end
if i have line 1 only in code not line 2 then this below code work fine for me
#answers = hash_answers.sort_by { |key, value| value }.reverse
but i also want to sort it by craeted_at
How i can achive this or make hash in another way
Any help will be most appreciated
Thanks
answers.sort_by do |ans|
[ans.net_votes, ans.created_at]
end
Then in your Answers class
def net_votes
votes_up_by_all_users - votes_down_by_all_users
end
You shouldn't have to pass an object to itself as a variable as in ans.votes_up_by_all_users(ans). Objects always know about themselves.
Usually you can sort on a number of things by creating an array of these things and use that as your sort key:
#answers = hash_answers.sort_by { |k, v| [ v[:created_at], v[:count] }
This is dependent on having a sortable structure to start with. You're jamming two entirely different things into the same hash. A better approach might be:
hash_answers[ans.id] = {
:id => ans.id,
:count => voted_up_users.count -voted_down_users.count,
:created_at => ans.created_at
}
You can adjust the order of the elements in the array to sort in the correct order.
This has been bugging me for a while. It's not a difficult thing, but I don't know why there's no easy way to do it already, and I bet there is and I don't see it.
I just want to take a hash, like this:
cars = {:bob => 'Pontiac', :fred => 'Chrysler',
:lisa => 'Cadillac', :mary => 'Jaguar'}
and do something like
cars[:bob, :lisa]
and get
{:bob => 'Pontiac', :lisa => 'Cadillac'}
I did this, which works fine:
class Hash
def pick(*keys)
Hash[select { |k, v| keys.include?(k) }]
end
end
ruby-1.8.7-p249 :008 > cars.pick(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
There's obviously a zillion easy ways to do this, but I'm wondering if there's something built in I've missed, or a good an un-obvious reason it's not a standard and normal thing? Without it, I wind up using something like:
chosen_cars = {:bob => cars[:bob], :lisa => cars[:lisa]}
which isn't the end of the world, but it's not very pretty. It seems like this should be part of the regular vocabulary. What am I missing here?
(related questions, include this: Ruby Hash Whitelist Filter)
(this blog post has precisely the same result as me, but again, why isn't this built in? http://matthewbass.com/2008/06/26/picking-values-from-ruby-hashes/ )
update:
I'm using Rails, which has ActiveSupport::CoreExtensions::Hash::Slice, which works exactly as I want it to, so problem solved, but still... maybe someone else will find their answer here :)
Just to help others, some years after the fact:
Slice works nicely to do what I wanted.
> cars.slice(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
I've always thought that was a weird omission as well, but there really is no simple, standard method for that.
Your example above might be unnecessarily slow because it iterates over all the hash entries whether we need them or not, and then repeatedly searches through the keys parameter array. This code should be a bit faster (assuming that would ever matter -- and I haven't tried to benchmark it).
class Hash
def pick(*keys)
values = values_at(*keys)
Hash[keys.zip(values)]
end
end
select deserves at least to be mentioned:
cars = {:bob => 'Pontiac', :fred => 'Chrysler',
:lisa => 'Cadillac', :mary => 'Jaguar'}
people = [:bob, :lisa]
p cars.select{|k, _| people.include?(k)}
#=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
Ruby makes it possible to add that feature without much pain:
class Hash
alias old_accessor :[]
def [](*key)
key.is_a?(Array) ? self.dup.delete_if{|k, v| !key.include? k} : old_accessor(key)
end
end
I hope this helps. I know it's not a built-in feature.
{ height: '178cm', weight: '181lbs', salary: '$2/hour' }.select { |k,v| [:height, :weight].include?(k) }
I'm new to Ruby and am working on a CLI application that parses some reports of mine. I would like to figure out the most efficient way to achieve the following with this line:
MAXCONN: 2000, MAXSSL_CONN: 500, PLAINCONN: 34, AVAILCONN: 1966, IDLECONN: 28, SSLCONN: 0, AVAILSSL: 500
I would like to map this into a hash accordingly:
{ :maxconn => 2000, :maxssl_conn => 500, :plainconn => 34, :availconn => 1966, :idleconn => 28, :sslconn => 0, :availssl => 500 }
The only way I can think to do this is to split at the comma and then again at the semi-colon and map them.
I have a sneaking suspicion there may be some Ruby magic to achieve this in a more efficient and less cumbersome way.
Any input and or tricks / tips would be appreciated as I have a feeling I'll be approaching problems like this relatively often.
We combine the technique for converting a bunch of key-value pairs into a hash
Hash[[[k1, v1], [k2, v2], ...]] #=> { k1 => v1, k2 => v2, ... }
with the regular expression method String#scan, which passes through a string and collects matches in an array, to get
Hash[reports.scan(/(\w+): (\w+)/).map { |(first, second)| [first.downcase.to_sym, second.to_i] }]
This also uses a block with Enumerable#map that interprets arrays as pairs of (first, second) elements in the argument list, extracts these into new elements, and applies conversions to them so as to tailor the resulting hash to your example's specifications (otherwise, you just get a hash of strings mapping to strings).
hash = {}
input.scan /(\w)+\:(\d+)\,/ do |key, val|
hash[key.downcase.to_sym] = val.to_i
end
Seems like the most obvious.
You could do (with some caveats) this:
hash = Hash[*eval(string).map{|k,v| [k.downcase.to_sym, v]}.flatten]
But that's really forcing a one-liner where it shouldn't be.
Of course splitting it isn't a horrible idea:
hash = input.split(",").each_with_object({}) do |str, h|
k,v = str.split(":")
h[k.downcase.to_sym] = v.to_i
end
If you are using ruby1.9, you do not even need to parse that text because ruby1.9 accepts hash literals in the form {symbol: value} when the key is a symbol. Simply, do this:
eval("{#{your_string.downcase}}")
Write the code first, then optimize it
If your code causes a bottleneck on your implementation of Ruby, the first thing you'd have to do is work out if it's the string splitting, or the hash creation, or adding your new hash to whatever array it's in (this is the step I'd be most worried about!), that's causing the bottleneck.
Write functional programming style code - it's usually cleaner, and sometimes faster
With regards to adding your new hash to the array it's in, generally you shouldn't do
array_of_hashes = []
lines.each do |line|
hash = create_hash(line)
array_of_hashes << hash
end
instead, do
array_of_hashes = lines.map do |line|
hash = create_hash(line)
hash
end
The latter is generally better performing, but regardless of that, it's cleaner code, so it's not a premature optimization.