Сокращение массива хэшей в новый хэш - PullRequest
0 голосов
/ 06 октября 2018

У меня есть отношение ActiveRecord, которое выглядит примерно так:

[  
  {  
    timestamp: Tue, 02 Oct 2018 00:00:00 PDT -07:00,
    user_id: 3,
    organization_id: 1,
    all_sales: 10,
    direct_sales: 7,
    referred_sales: 3,
  },
  {
    timestamp: Wed, 03 Oct 2018 00:00:00 PDT -07:00,
    user_id: 3,
    organization_id: 1,
    all_sales: 17,
    direct_sales: 8,
    referred_sales: 9,
  },  
  {
    timestamp: Thu, 04 Oct 2018 00:00:00 PDT -07:00,
    user_id: 3,
    all_sales: 3,
    direct_sales: 3,
    referred_sales: 0,
  }
]

Я хотел бы создать «сумму» всех ключей, относящихся к продажам (для наших целей здесь яне нужно timestamp, user_id или organization_id, так что в целом я хотел бы закончить примерно так:

{
  all_sales: 30
  direct_sales: 18
  referred_sales: 12
}

Есть ли элегантный рубиновый способ сделать это«Я мог бы легко создать набор переменных для каждой категории продаж и дополнить их, пока я перебираю исходное отношение, но я хотел посмотреть, есть ли у сообщества более чистый метод. На самом деле каждый из этих хэшей имеет более 3 релевантных ключей.и поэтому я боюсь, что такой подход очень быстро испортится.

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

Ответы [ 5 ]

0 голосов
/ 06 октября 2018

Несколько более подробных других опций (которые можно сделать более сухими и общими):

result = { all_sales: (array.sum{ |e| e[:all_sales] }), direct_sales: (array.sum{ |e| e[:direct_sales] }), referred_sales: (array.sum{ |e| e[:referred_sales] }) }

или:

result = array.each.with_object(Hash.new(0)) do |h, obj|
  obj[:all_sales] += h[:all_sales]
  obj[:direct_sales] += h[:direct_sales]
  obj[:referred_sales] += h[:referred_sales]
end

Чтобы быть более сухими и общими, начиная смассив требуемых ключей

keys = [:all_sales, :direct_sales, :referred_sales]

Первый становится

keys.map.with_object(Hash.new) { |k, obj| obj[k] = array.sum { |e| e[k] } }

, а второй:

array.each.with_object(Hash.new(0)) { |h, obj| keys.each { |k| obj[k] += h[k] } }
0 голосов
/ 06 октября 2018

Использование функции слияния и уменьшения

value = arr.reduce do |h1, h2|
  h1.merge(h2) do |k, v1, v2|
    [:all_sales, :direct_sales, :referred_sales].include?(k) ? (v1 + v2) : nil
  end
end.reject {|_, v| v.nil?}

p value
0 голосов
/ 06 октября 2018

Поскольку вы начинаете с ActiveRecord Relation, вы можете использовать pluck , чтобы вычислить все суммы с помощью SQL и вернуть массив с вашими итогами:

SalesModel.pluck('SUM(all_sales)', 'SUM(direct_sales)', 'SUM(referred_sales)')
#=> [30, 18, 12]
0 голосов
/ 06 октября 2018

Или используйте функциональный подход с reduce и merge методами:

keys = %i{all_sales direct_sales referred_sales}
total_sales = items.map {|item| item.select{|key, _| keys.include?(key)}}
                   .reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}

# total_sales
# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}

Или более понятный подход для Ruby 2.5.0 или выше, благодаря @Johan Wentholt

items.map {|item| item.slice(:all_sales, :direct_sales, :referred_sales)}
     .reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}

# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}
0 голосов
/ 06 октября 2018

Это будет работать:

arr.each_with_object({}) do |obj, hash|
  %i[all_sales direct_sales referred_sales].each do |sym|
    hash[sym] = hash[sym].to_i + obj[sym]
  end
end

Это одна итерация, вы можете написать вложенный цикл в виде 3 разных строк, но, на мой взгляд, он немного чище.

Примечание:вызов to_i при получении предыдущего значения hash[sym], так как изначально это nil и nil.to_i == 0.Кроме того, вы можете инициализировать все неизвестные значения с 0, например:

arr.each_with_object(Hash.new(0)) do |obj, hash|
  %i[all_sales direct_sales referred_sales].each do |sym|
    hash[sym] += obj[sym]
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...