Create Hash iterating an array of objects - ruby-on-rails

I have an object that has attributes name and data, among others. I want to create a hash that uses the name as the key and the data (which is an array) as the value. I can't figure out how to reduce the code below using map. Is it possible?
def fc_hash
fcs = Hash.new
self.forecasts.each do |fc|
fcs[fc.name] = fc.data
end
fcs
end

Use Hash[]:
Forecast = Struct.new(:name, :data)
forecasts = [Forecast.new('bob', 1), Forecast.new('mary', 2)]
Hash[forecasts.map{|forecast| [forecast.name, forecast.data]}]
# => {"mary"=>2, "bob"=>1}

def fc_hash
forecasts.each_with_object({}) do |forecast, hash|
hash[forecast.name] = forecast.data
end
end

I always use inject or reduce for this:
self.forecasts.reduce({}) do |h,e|
h.merge(e.name => e.data)
end

Hash[*self.forecases.map{ [fc.name, fc.data]}.flatten]

With Ruby 2.1 and onward, you could also use Array#to_h
self.forecasts.to_h { |forecast| [forecast.name, forecast.data] }

Related

how append an object to association relation in rails?

In a rails 4.1 application I need to add an object to an "AssociationRelation"
def index
employee = Employee.where(id_person: params[:id_person]).take
receipts_t = employee.receipts.where(:consent => true) #gives 3 results
receipts_n = employee.receipts.where(:consent => nil).limit(1) #gives 1 result
#I would need to add the null consent query result to the true consent results
#something similar to this and the result is still an association relation
#receipts = receipts_t + receipts_n
end
Is there a simple way to do this?
A way of solving this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
receipts_t = employee_receipts.where(consent: true)
receipts_n = employee_receipts.where(consent: nil).limit(1)
#receipts = Receipt.where(id: receipts_t.ids + receipts_n.ids)
end
Unfortunately .or() can't be used here because it's only available from Rails v5.0.0.1
you could do this way
receipts_t_ids = employee.receipts.where(:consent => true).pluck(:id)
receipts_n_ids = employee.receipts.where(:consent => nil).limit(1).pluck(:id)
#receipts = Receipt.where(id: receipts_t_ids + receipts_n_ids)
To avoid extra queries and keeping arrays in memory, you can use or
Like this:
def index
employee_receipts = Employee.find_by(id_person: params[:id_person]).receipts
#receipts =
employee_receipts.where(consent: true).or(
employee_receipts.where(consent: nil).limit(1)
)
end

rails controller map unknown

In authors_controller.rb, I have this :
def show
a = Author.find(params[:id])
#author = a.map { |e| e.titlecase }
end
I get an error say that map is an undefined method for Author::0x007fec244142a0.
I also tried this :
def show
#author = Author.find(params[:id])
#author.each { |k, v| v.capitalize }
end
How can I apply the method titlecase to each value of Author.find ?
find(params[:id]) returns not array, not enumerator and not Relation class but just instance of your model. You can't use map or each so just apply titlecase to returned object.
def show
#author = Author.find(params[:id])
#author.name = #author.name.titlecase # if you have column 'name'
end
But better move titlecased name to model's method or just use #author.name.titlecase where it's needed.
You can use where and use map operator with it:
def show
#author = Author.where(id: params[:id])
It's ugly but it works. I'm sure there is a better way to do this stuff.
#author.attributes.map do |k,v|
v = #author.__send__(k).capitalize if #author.__send__(k).respond_to?(:capitalize)
end
#author.save
I must say however that I wouldn't recommend doing things this way. Better to capitalize each field in the model
From I understood. You want to capitalize all fields of record Author.find(params[:id]) right?
First, Author.find(params[:id]) will return a record, not array. That means you can't use each or map for it.
To capitalize all fields of a record. Could u try:
def show
author = Author.find(params[:id])
#author = author.attributes.values.map{|field| field.to_s.capitalize}
end
It will return an array of all field values.
UPDATE 1
For better
def show
author = Author.find(params[:id])
#author_info = author.attributes.values.map{|field| field.is_a?(String) ? field.capitalize : field}
end

Return Array in a helper method

Why can't I return an array in a helper method?
def childrenOf(a)
#children = Post.find_by_parent_id(a.id)
return #children
end
Thanks in advance
You can.
Use find_all_by_parent_id instead.
And you don't need the second line.
The following is enough.
def childrenOf(a)
#children = Post.find_all_by_parent_id(a.id)
end
In Rails 3 instead of using find_all_by use where:
def childrenOf(a)
#children = Post.where(:parent_id => a.id)
end

Using a method while looping through an array in ruby

I am using ruby-aaws to return Amazon Products and I want to enter them into my DB. I have created a model Amazonproduct and I have created a method get_amazon_data to return an array with all the product information. When i define the specific element in the array ( e.g. to_a[0] ) and then use ruby-aaws item_attributes method, it returns the name I am searching for and saves it to my DB. I am trying to iterate through the array and still have the item_attributes method work. When i don't define the element, i get this error: undefined method `item_attributes' for #Array:0x7f012cae2d68
Here is the code in my controller.
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each { |name|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = #arr.item_attributes.title.to_s
}
EDIT: Code in my model to see if that helps:
class Amazonproduct < ActiveRecord::Base
def self.get_amazon_data(r)
resp = Amazon::AWS.item_search('GourmetFood', { 'Keywords' => 'Coffee Maker' })
items = resp.item_search_response.items.item
end
end
Thanks for any help/advice.
I'm not familiar with the Amazon API, but I do observe that #arr is an array. Arrays do not usually have methods like item_attributes, so you probably lost track of which object was which somewhere in the coding process. It happens ;)
Try moving that .item_attributes call onto the object that supports that method. Maybe amazonproduct.get_amazon_data(:r), before its being turned into an array with to_a, has that method?
It's not quite clear to me what your classes are doing but to use #each, you can do something like
hash = {}
[['name', 'Macbook'], ['price', 1000]].each do |sub_array|
hash[sub_array[0]] = sub_array[1]
end
which gives you a hash like
{ 'name' => 'Macbook', 'price' => 1000 }
This hash may be easier to work with
#product = Product.new
#product.name = hash[:name]
....
EDIT
Try
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each do |aws_object|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = aws_object.item_attributes.title.to_s
end
end

Ruby on Rails: Array to Hash with (key, array of values)

Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]

Resources