Rails, converting an integer to array offset - ruby-on-rails

Based on feedback, I am revising this question. How do i convert an integer array to a set of values displayed on the view page using the Constants (defined in the model). I can do it on my form page but have not figured it out for the Index.
On an Index Page (if dbase has grades: [0, 1, 2], the page should display as A+, A, B)
something like what is done for days of the week (e.g. http://hightechsorcery.com/2010/02/16/ruby-arrays-and-hashes-and-days-of-the-week/
....
<h4 class="h3"><%= #gradestemp %>
CONTROLLER Labels Controller
def index
#labels = current_user.labels
grad = []
#gradestemp = Contact::GRADES.each_with_index { |x, i| grad << [x, i] }
render
end
MODEL NB: GRADES is a constant - I am trying to also use in Labels
class Label < ActiveRecord::Base
NB: this is in the CONTACT model
GRADES = [["A+",0 ], ["A",1], ["B", 2], [ "C",3], [ "D",4], [ "-",5]]
Am i able to access the Contact GRADES while in the Labels controller?
i have found this SO - which is similar to what i am trying to do:
Ruby: How to store and display a day of the week?
Based suggestion below, this did the trick:
<h4 class="h3"><%= print_campaign.grades.compact.map{|idx| Contact::GRADES[idx][0]}.join(' ') %>

[0, 1, 2].map {|g| Contact::GRADES.select.map {|letter, val| val == g; letter}
Really though GRADES seems much better off as a Hash:
GRADES = {
"A+" => 0,
"A" => 1,
..
}
Then you lookup would be much simpler
[0, 1, 2].map {|g| Contact::GRADES.key(g) }

Related

How to calculate specific rating count hash in ruby on rails?

So, I have an after_save hook on review model which calls calculate_specific_rating function of product model. The function goes like this:
def calculate_specific_rating
ratings = reviews.reload.all.pluck(:rating)
specific_rating = Hash.new(0)
ratings.each { |rating| specific_rating[rating] += 1 }
self.specific_rating = specific_rating
save
end
Right now, it returns
specific_rating => {
"2"=> 3, "4"=> 1
}
I want it to return like:
specific_rating => {
"1"=> 0, "2"=>3, "3"=>0, "4"=>1, "5"=>0
}
Also, is it okay to initialize a new hash everytime a review is saved? I want some alternative. Thanks
You can create a range from 1 until the maximum value in ratings plus 1 and start iterating through it, yielding an array where the first element is the current one, and the second element is the total of times the current element is present in ratings. After everything the result is converted to a hash:
self.specific_rating = (1..ratings.max + 1).to_h { |e| [e.to_s, ratings.count(e)] }
save
You could also do something like this -
def calculate_specific_rating
ratings = [1,2,3,4,5]
existing_ratings = reviews.group_by(&:rating).map{|k,v| [k, v.count]}.to_h
Hash[(ratings - existing_ratings.keys).map {|x| [x, 0]}].merge(existing_ratings)
end
which gives
{3=>0, 4=>0, 5=>0, 2=>3, 1=>1}

Using .map function to create hashes

I have an array [5,2,6,4] and I would like to create a structure such as the first minus the second etc until the last row.
I have tried using map, but not sure how to proceed since i might need indxes.
I would like to store the result in something that looks like:
{1 => (5, 2, 3), 2 =>(2,6,-4), 3 => (6,4,2)}
So an array of x should return x-1 hashes.
Anybody knows how to do? should be a simple one.
Thank you.
First, you want to work with the array elements in pairs: 5,2, 2,6, ... That means you want to use each_cons:
a.each_cons(2) { |(e1, e2)| ... }
Then you'll want the index to get the 1, 2, ... hash keys; that suggests throwing a Enumerator#with_index into the mix:
a.each_cons(2).with_index { |(e1, e2), i| ... }
Then you can use with_object to get the final piece (the hash) into play:
a.each_cons(2).with_index.with_object({}) { |((e1, e2), i), h| h[i + 1] = [e1, e2, e1 - e2] }
If you think all the parentheses in the block's arguments are too noisy then you can do it in steps rather than a single one-liner.
You can use each_index:
a = [5, 2, 6, 4]
h = {}
a[0..-2].each_index { |i| h[i+1] = [a[i], a[i+1], a[i] - a[i+1]] }
h
=> {1=>[5, 2, 3], 2=>[2, 6, -4], 3=>[6, 4, 2]}
Try to use
each_with_index
Suppose you have an array:
arr = [3,[2,3],4,5]
And you want to covert with hash(key-value pair). 'Key' denotes an index of an array and 'value' denotes value of an array. Take a blank hash and iterate with each_with_index and pushed into the hash and finally print the hash.
Try this:
hash={}
arr.each_with_index do |val, index|
hash[index]=val
end
p hash
Its output will be:
{0=>3, 1=>[2, 3], 2=>4, 3=>5}
If you want that index always starts with 1 or 2 etc then use
arr.each.with_index(1) do |val, index|
hash[index] = val
end
Output will be:
{1=>3, 2=>[2, 3], 3=>4, 4=>5}

Ruby on Rails group_by is not grouping in the correct order using DateTime

First I get a collection of channels from my scope
Channel.not_archived.map { |c| channels << c }
Then I sort those by the start_time attribute:
channels.sort! { |a, b| a.start_time <=> b.start_time }
Then I want to group them by their start times. So channels that start at 8:00am will be grouped together. So I use the group_by method:
#grouped_channels = #channels.group_by { |c| time_with_offset(c).strftime("%I:%M %P") }
the time_with_offset method:
# Returns time with offset
def time_with_offset(channel)
user = current_user.time_zone.to_i
organization = channel.organization.time_zone.to_i
time_offset = organization -= user
channel.start_time - time_offset.hours
end
And I get back all of my records in the correct group. The issue i'm having is that the groups are not in order. So the group of 8:00am should be before the group of 9:00am. It's just in weird random order now. Can anyone help me get these in correct order?
If you wish to reorder the key-value pairs of any hash h in key order given by an array keys, which contains all the keys in the desired order, write
(keys.zip(h.values_at(*keys))).to_h
For Ruby versions prior to 2.0, write
Hash[keys.zip(h.values_at(*keys))]
For example,
h = { b: 1, d: 2, a: 3, c: 4 }
#=> {:b=>1, :d=>2, :a=>3, :c=>4}
keys = [:a, :b, :c, :d]
(keys.zip(h.values_at(*keys))).to_h
#=> {:a=>3, :b=>1, :c=>4, :d=>2}
The steps are as follows.
a = h.values_at(*keys)
#=> same as h.values_at(:a, :b, :c, :d)
#=> [3, 1, 4, 2]
b = keys.zip(a)
# => [[:a, 3], [:b, 1], [:c, 4], [:d, 2]]
b.to_h
#=> {:a=>3, :b=>1, :c=>4, :d=>2}
First you are sorting by one time, then you are grouping by a different time. I expect this explains your undesired order.
Sort by the offset time.
channels.sort_by { |c| time_with_offset(c) }.group_by { |c| time_with_offset(c).strftime("%I:%M %P") }

Find intersection of arrays

I'm having trouble making a simple form that finds the intersection of two arrays. The end goal is to find the intersection of two arrays of emails, but right now i'm simply testing with integers. Everything works in the controller, and if I hard code the arrays in the view I get the correct result. Below is my code
Rails console, everything is kosher:
1.9.3p374 :011 > _a
=> [1, 2, 3, 4]
1.9.3p374 :012 > _b
=> [1, 2, 1, 1, 1]
1.9.3p374 :013 > c = _a & _b
=> [1, 2]
When I try pass the same values from a form, I get an empty array result (I'm passing both arrays into the view to ensure they're there.
Controller:
def intersect
#array1 = [params[:a]]
#array2 = [params[:b]]
#intersection = #array1 & #array2
end
Code in View:
Array 1: <%= #array1 %> <br>
Array 2: <%= #array2 %><br>
Intersection: <%= #intersection %>
Result in browser:
Array 1: ["1,2,3,4,5"]
Array 2: ["1,2,2,3,3"]
Intersection: []
Since I can get this to work hardcoded I'm sure i'm doing something newbish! Any help is beyond welcome!!!
Your arrays each contain a single element which is the string "1,2,3,4,5". You probably want an array with 5 elements instead ([1,2,3,4,5]). You can do so by splitting the array on a comma:
#array1 = params[:a].split(',')
#array2 = params[:b].split(',')
#intersection = #array1 & #array2
#=> ["1", "2", "3", "4", "5"]

Trouble sorting records on unique attributes

Working with the following code, I need to return only records where the `point' attribute is unique. I can't seem to get there by myself.
uniques = Item.find_all_by_item_id(item_id)
uniques.sort! {|a, b| b.point <=> a.point } # how do I reject the equal points?
In other words.. I guess, how do you make [0, 1, 1, 1, 2, 3, 3, 4, 4, 7] #=> [0, 2, 7] ?
How about this:
# Get the number of items with each point value
counts = Item.count("point", :group => "point")
# Get the IDs of all entries that only show up once
unique_ids = counts.collect { |count| count[0] if count[1] == 1 }.compact
unique_items = Item.find_all_by_id(unique_ids)
I can think of few ways off the top of my head to do this:
uniques.reject!{|u| uniques.select{|x| x == u}.size > 1}
Basically iterate through the uniques array and then see if there is more than one of those items in the array. Obviously there are lots of clever ways to speed this up, but for small arrays this should work.
or
h = Hash.new(0)
uniques.each{|u| h[u] += 1}
h.reject{|k,v| v > 1}.keys
Basically count how many times each item shows up in a hash, if its more than one reject it and then just look at the keys.

Resources