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

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

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

Заранее извиняюсь за нуб-код :)

Вот 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

Таблица из views\adwordscampaign\index.html.erb

<table class="table table-striped table-bordered">
  <tr>
    <th>ID
    <th>Name
    <th>Status
    <th>Impressions
    <th>Clicks
    <th>CTR
    <th>Cost

<% @campaigns.each do |id, campaign| %>
  <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>



<% end %>     

<td><%= @campaigns %></td>

</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:0x3e9ibac8 @id=109880145, @name="Competitors", @status="ACTIVE", @cost=1596, @costdecimal=15.96, @impressions=515, @clicks=5, @ctr=0.9708737864077669>

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

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

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


person macoughl    schedule 10.09.2013    source источник
comment
Нам не нужны ERB и код модели. Нам нужен образец данных, которые вы получаете, урезанный до самого необходимого.   -  person the Tin Man    schedule 11.09.2013


Ответы (1)


@campaigns выглядит как простой хеш {id -> Adwordscampaign}. Нотация #<ObjectName:data> означает, что 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(&:+)

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

person Nick Veys    schedule 10.09.2013
comment
Идеальный! Спасибо за дополнительную информацию в вашем ответе! - person macoughl; 11.09.2013