Сумма идентичных ключей в Ruby Hash

У меня есть простое приложение, предназначенное для вытягивания показателей Adwords (через adwordsapi) и представления его пользователю в таблице внутри представления Rails. Он работает правильно, снимая всю информацию для нескольких кампаний, за исключением одной проблемы.

Я не могу получить итоги каждого поля (общая стоимость, общее количество показов и т.д.). Я могу показывать показы для каждой кампании в учетной записи, но не могу получить итоговые данные для учетной записи.

Я заранее прошу прощения за код noob, который нужно выполнить :)

Вот adwordscampaign_controller.rb

class AdwordscampaignController < ApplicationController

PAGE_SIZE = 50

def index()
@selected_account = selected_account

if @selected_account
 response = request_campaigns_list()
 if response
 @campaigns = Adwordscampaign.get_campaigns_list(response)
 @campaign_count = response[:total_num_entries]
 @start = params[:start]
 @end = params[:end]

 @myhash = Adwordscampaign.get_campaigns_list(response)

 end
 end
end

private

def request_campaigns_list()

# Prepare start and end date for the last week.

if params[:start].nil?
start_date = DateTime.parse((Date.today - 7).to_s).strftime("%Y%m%d")
end_date = DateTime.parse((Date.today - 1).to_s).strftime("%Y%m%d")
else
start_date = params[:start]
end_date = params[:end]
end

api = get_adwords_api()
service = api.service(:CampaignService, get_api_version())
selector = {
 :fields => ['Id', 'Name', 'Status', 'Cost', 'Impressions', 'Clicks', 'Ctr', 'Conversions', 'Amount'],
 :ordering => [{:field => 'Id', :sort_order => 'ASCENDING'}],
 :date_range => {:min => start_date, :max => end_date},
 :paging => {:start_index => 0, :number_results => PAGE_SIZE}
}
result = nil


begin
 result = service.get(selector)
rescue AdwordsApi::Errors::ApiException => e
 logger.fatal("Exception occurred: %s\n%s" % [e.to_s, e.message])
 flash.now[:alert] =
 'API request failed with an error, see logs for details'
end
return result
end
end

соответствующая модель: adwordscampaign.rb

class Adwordscampaign
 attr_reader :id
 attr_reader :name
 attr_reader :status
 attr_reader :cost
 attr_reader :impressions
 attr_reader :clicks
 attr_reader :ctr
 attr_reader :costdecimal
 attr_reader :costperconversiondecimal


def initialize(api_campaign)
 @id = api_campaign[:id]
 @name = api_campaign[:name]
 @status = api_campaign[:status]

 budget = api_campaign[:budget]

 stats = api_campaign[:campaign_stats]

 @cost = (stats[:cost][:micro_amount] / 10000)
 @costdecimal = (@cost * 10000).round.to_f / 1000000
 @impressions = stats[:impressions]
 @clicks = stats[:clicks]
 @ctr = (stats[:ctr] * 100)

end


def self.get_campaigns_list(response)
 result = {}
 if response[:entries]
 response[:entries].each do |api_campaign|
 campaign = Adwordscampaign.new(api_campaign)
 result[campaign.id] = campaign
 end
 end
 return result
end
end

Таблица из представлений \adwordscampaign\index.html.erb

<% end %> 

<table>
 <tbody><tr>
 <th>ID
 </th><th>Name
 </th><th>Status
 </th><th>Impressions
 </th><th>Clicks
 </th><th>CTR
 </th><th>Cost

<% @campaigns.each do |id, campaign| %>
 </th></tr><tr>
 <td><%= campaign.id %></td>
 <td><%= campaign.name %></td>
 <td><%= campaign.status %></td>
 <td><%= number_with_delimiter(campaign.impressions) %></td>
 <td><%= number_with_delimiter(campaign.clicks) %></td>
 <td><%= number_with_precision(campaign.ctr, precision: 2) %>%</td>
 <td>$<%= number_with_delimiter(campaign.costdecimal) %></td><td><%= @campaigns %></td>

</tr></tbody></table>

Я бросил @campaigns в представлении (внизу), чтобы я мог видеть, что это за результат. Синтаксис для меня немного незнакомый, но он, похоже, хешируется в хэш (правильно?)

вывод @campaigns в представлении

{109886905=>#<adwordscampaign:0x4528ba0 @id="109879905," @name="Upholstery Cleaning" ,="" @status="ACTIVE" @cost="2702," @costdecimal="27.02," @impressions="824," @clicks="7," @ctr="0.8495145631067961">, 103480025=>#<adwordscampaign:0x7028b28 @id="109880025," @name="Carpet Cleaning" ,="" @status="ACTIVE" @cost="16739," @costdecimal="167.39," @impressions="4457," @clicks="29," @ctr="0.6506618801884676">, 104560145=>#</adwordscampaign:0x7028b28></adwordscampaign:0x4528ba0>

Наконец, на вопрос: как получить общие @clicks (или @impressions, @cost и т.д.) Для каждой из трех кампаний, найденных здесь?

Я искал такие вещи, как "как суммировать идентичные хэш-ключи/значения" или "как объединить вложенные хэши" безрезультатно.

Заранее спасибо!

1 ответ

@campaigns выглядит как простой хэш {id → Adwordscampaign}. Обозначение # - это Ruby, который делает все возможное, чтобы дать вам что-то читаемое для этого объекта.

Например, суммирование кликов - это простое сокращение карты:

total_clicks = @campaigns.map { |id, campaign| campaign.clicks } .reduce(&:+)

Это создает массив всех значений campaign.clicks, а затем объединяет их все с помощью оператора +.

Это предполагает, что на этих объектах есть #clicks. Если нет, настройте соответственно.

Если все, что вы делаете, суммирует различные атрибуты, вы можете упростить его использование несколько раз так:

camp_list = @campaigns.map { |id, campaign| campaign }
total_clicks = camp_list.map(&:clicks).reduce(&:+)
total_cost = camp_list.map(&:cost).reduce(&:+)
total_impressions = camp_list.map(&:impressions).reduce(&:+)

Дальнейшее сокращение - это упражнение для читателя. :)

licensed under cc by-sa 3.0 with attribution.