I have hash for example
{ 1 => 5, 3 => 6, 5 => 5, 8 => 10, 11 => 11}
and I have one key - 5, and I need get hash with next three key-value. in this case result will be:
{ 8 => 10, 11 => 11, 1 => 5}
How i can do this?
That is not a usual use case for a hash table. The whole point is to be able to look up specific keys efficiently, not iterate over keys in sequence.
If you want to do that, you'll need to choose another data structure, either instead of a hash or alongside the hash (if you still wish for efficient lookup).
If you know that they're integer keys, you could test for the existence of subsequent ones until you find three but that's pretty inefficient, especially if the current one is the second highest, for example. You would be better maintaining a different data structure.
as others have said you can't get the 'next' pair of values. If you're looking specifically for numerically ordered pairs where the keys are all numbers, you could do something like this:
h = { 1 => 5, 3 => 6, 5 => 5, 8 => 10, 11 => 11}
sorted_keys = h.keys.sort
sorted_keys.each do |key|
p "#{key} = #{h[key]}"
end
which returns:
"1 = 5"
"3 = 6"
"5 = 5"
"8 = 10"
"11 = 11"
You can't.
Ruby hashes are unordered. There's no reliable "next" key/value.
Related
I do hope the title is correct. Using a Rails 5 project with PostgreSQL and Ruby 2.3.1.
I have enabled hstore in my app. I do not know the correct way to update a table column with an object data. Make sense?
# There will be only two arrays:
cars = ["honda", "bmw"]
rate = [1, 2]
cars.zip(rate).map do |c,r|
Foo.find(1).update_attributes(bar: {c => r})
end
# Foo.find(1).bar = {"bmw" => 2}
I expect:
# Foo.find(1).bar = {"honda" => "1", "bmw" => 2}
How to get the two values into bar?
I was trying to fit one of my previous questions into this but not sure where to start.
You can create a hash from the array
a = cars.zip(rate)
Foo.find(1).update_attributes(bar: Hash[a])
or if you need some enumerator
cars.zip(rate).each_slice(2) { |x| Foo.find(1).update_attributes(bar: Hash[x]) }
I'm new to Ruby and I've run into an issue I can't solve.
I'm trying to use gsub() to match a pattern in a string, then use that match as an index into a hash. So far, I haven't been able to figure it out. Here's some code:
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment = "There are 'pig_num' pigs on this farm"
assessment.gsub(/'(.+?)'/, '\1') # => "There are pig_num pigs on this farm"
assessment.gsub(/'(.+?)'/, farm) # => "There are pigs on this farm"
assessment.gsub(/'(.+?)'/, farm['\1']) # => TypeError: no implicit conversion of nil into String
assessment.gsub(/'(.+?)'/) { |key| farm[key] }
The first call to gsub() shows that I am matching the string I want.
The second call is an attempt to use the gsub(pattern, hash) flavor found at the Ruby documentation site.
The third call is trying to reference the value using the match as an index.
The fourth is some fancy pants way I thought might work using a lambda/proc/block.
What am I doing wrong?
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment = "There are 'pig_num' pigs on this farm"
1
"You may want to get the first object from farm hash but you need to tell from which hash you want to retrieve value". Otherwise, you need to use just Integer with String type like above.
assessment.gsub(/'(.+?)'/, '1')
2
when you 'gsub' string, you get 'pig_num' because you include '' inside the regex so that result would be "'pig_num'". However, the key of hash is "pig_num". "pig_num" and "'pig_num'" are different. That is why you can't get data properly.
assessment.gsub(/'(.+?)'/, farm)
3
You can not point index inside blacket but hash key
assessment.gsub(/'(.+?)'/, farm["pig_num"].to_s)
4
As I said before, you get "'pig_num'" as key. If you print out the key value inside the block, you will see it. You need to change it to 'Pure key'. To get rid of quotation, you can use gsub again and make it empty instead of quotation. gsub! is a destructive method which means modifies original value. If you use just gusb, the method returns modified value but the original key itself (in this situation) does not change. Otherwise, you need to assign the new value to another variable.
assessment.gsub(/'(.+?)'/) { |key| p key.gsub!("\'", ""); farm[key] }
I hope this answer is helpful for you. Cheers
Try this
assessment.gsub(/#{farm.keys.join('|')}/, farm)
I see on your code with regex match it will recognize 'pig_num' is the hash key to find on farm hash. So you need to change your hash like
farm = { "'pig_num'" => 5, "'horse_num'" => 2, "'cow_num'" => 4} or you can change your regex. Example
farm = { "'pig_num'" => 5, "'horse_num'" => 2, "'cow_num'" => 4}
assessment.gsub(/'(.+?)'/, farm) # => "There are 5 pigs on this farm"
Or
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment.gsub(/pig_num/, farm) # => "There are '5' pigs on this farm"
With some minor adjustments, your can use sprintf's %{name} or %<name>snotation:
farm = { pig_num: 5, horse_num: 2, cow_num: 4 }
assessment = "There are '%{pig_num}' pigs on this farm"
sprintf(assessment, farm)
#=> "There are '5' pigs on this farm"
I'm has books. Each book has list of chapters. Each chapter has text. I'm need calculate total value of characters. I'm wrote such code:
symbols = 0
b.chapters.all.each do |c|
symbols += c.text.length
end
And that's work fine. But, when i wrought:
symbols = b.chapters.all.sum(:text.length)
It's return invalid count of chars. Did anyone has any suggestion where i'm wrong?
You could write using the block verion of #sum :
b.chapters.sum { |c| c.text.length }
This is wrong : b.chapters.all.sum(:text.length)
Because - :text.length gives you the length of symbol :text as 4. And the 4 is summed up n times, where n is the size of the collection b.chapters.
I tried with the data as I have in my project :
[21] pry(main)> Menu.first.dishes.count # => 5
[22] pry(main)> Menu.first.dishes.map { |d| d.dish_type.size } # => [9, 7, 5, 7, 6]
[23] pry(main)> Menu.first.dishes.sum { |d| d.dish_type.size } # => 34
I have has_many association between Dish and Menu. Now see the below another thing, which made your fool :
Menu.first.dishes.sum(:a.size) # => 5
Since I'm dealing quite a lot with durations in my Rails app, I would also like to use them as hash keys in some places. However, it does not seem to work as expected for me.
Creating the initial hash works fine. For example, the following will work:
>> hash = {1.week => 'abc', 1.month => 'def'}
However, retrieving the values from the hash is not possible:
>> hash[1.month]
=> nil
Some further investigation showed me the reason for this:
>> 1.week.eql? 1.week
=> false
Which was quite unexpected
Furthermore, it seems like these objects behave differently to normal objects of FixNum class. For normal FixNum objects, the value of the object_id seems to be always the same, e.g:
>> 15.object_id
=> 31
>> 15.object_id
=> 31
For Durations, this is different, although they are from the same class
>> 1.month.class
=> Fixnum
>> 1.month.object_id
=> 70305513092140
>> 1.month.object_id
=> 70305513086860
So, it seems like the objects are always different, which is why hashes will not work. The only solution is to access with exactly the same object:
>> a = 1.month
=> 1 month
>> hash = {a => 'b'}
=> {1 month=>"b"}
>> hash[a]
=> "b"
Obviously, this is not always possible if you have a lot of objects with dynamically created durations.
Grouping by durations does not work either:
>> limits.group_by(&:duration)
=> #<OrderedHash {1 month=>[#<Limit:0x7fe28e441380>], 1 month=>[#<Limit:0x7fe28e4290c8>]}>
So, I'm wondering whether it is possible to get durations working as hash keys somehow? So far I have not found a good solution and I'm not sure if there is one. The important this is that functions like (Time.now - duration) should keep working.
FYI: My Ruby version - 1.8.7, Rails version - 2.3.18
Wow, that is a weird finding. This does work, and I haven't been able to break it, but it seems a little fragile:
hash = {1.year.inspect => "val1", 2.month.inspect => "val2", (1.year-4.days).inspect => "val3"}
=> {"1 year"=>"val1", "2 months"=>"val2", "1 year and -4 days"=>"val3"}
hash[2.months.inspect]
=> "val2"
hash[(1.year-4.days).inspect]
=> "val3"
and to get the durations back
hash.keys.collect{|k| eval(k.gsub(" and ","+").split(" ").join("."))}
=> [1 year, 2 months, 1 year and -4 days]
I'm found a very ugly way to do what I need (currently just in the Rails view, I'll move it later), which is basically to find all the articles of a particular user, group them into their individual publications, and then sum the share counts of all of the articles the user has added for that publication.
...But it's not pretty. If there's a better way to do this, can someone advise?
=#user.articles.group(:publication).map do |p|
=p.publication
=#user.articles.where("publication = ?", p.publication).sum(:twitter_count)
=#user.articles.where("publication = ?", p.publication).sum(:facebook_count)
=#user.articles.where("publication = ?", p.publication).sum(:linkedin_count)
This gives the output (e.g.) NYT 12 18 14 BBC 45 46 47 CNN 75 54 78, which is pretty much what I need.
However, at present, it's also outputting some extra stuff on the end - "[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] sum(:twitter_count)" - I think this is to do with the .map but I'm not sure why.
Change = by - in first line.
- #user.articles.group(:publication).map do |p|
You are right. .map is creating new array.
Substitute = with - on the first line. (credits goes to juanpastas, I did not remember HAML)
Docs for #map:
Creates a new array containing the values returned by the block.
a = [ "a", "b", "c", "d" ]
a.map { |x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
a #=> ["a", "b", "c", "d"]