2015-12-06 6 views
0

У меня есть массив массивоврубин значение уник и сумма

arr = [ 
    ["BEER", 37], 
    ["BEER", 95], 
    ["BEER", 85], 
    ["BEER", 60], 
    ["BEER", 36], 

    ["FOOD", 31], 
    ["FOOD", 86], 
    ["FOOD", 50], 
    ["FOOD", 0] 
] 

Как извлечь массив как этот

new_arr = [ [ 'FOOD', sum_of_food ], 
      ['BEER', sum_of_beer] ] 

Я только что получил arr по Model.all.map {|c| [c.source, c.amount ] }

+0

Rails! = Ruby. Ваша проблема в Ruby, а не в Rails. –

+0

@SimoneCarletti Мне нужно это внутри моего приложения rails :) – xxx

+0

@xxx Это не имеет значения, это проблема Ruby. В этом случае факт, что ваш код находится в действии Rails или действии Sinatra, не имеет никакого значения. Вы управляете «массивом» с помощью Ruby, а не с Rails. –

ответ

3

Вы можете использовать Enumerable#inject в цикле каждого элемент в коллекции и вычислить отстойник в новый массив, который используется в качестве аккумулятора.

arr = [ 
["BEER", 37], 
["BEER", 95], 
["BEER", 85], 
["BEER", 60], 
["BEER", 36], 

["FOOD", 31], 
["FOOD", 86], 
["FOOD", 50], 
["FOOD", 0], 
] 

arr.inject({}) do |accumulator, (what, total)| 
    accumulator[what] ||= 0 
    accumulator[what] += total 
    accumulator 
end 

Если вам нужен Array вместо Hash, просто вызовите to_a на результат.


Если данные хранятся в базе данных, вы можете запросить непосредственно в базе данных с помощью оператора SQL SUM().

results = Model.select('source, SUM(amount) AS total').group(:source) 
result = results.first 
result.source 
# => FOO 
result.total 
# => the sum of amount for FOO 
+0

Спасибо, человек, удивительный ответ. – xxx

+1

Вариантом является 'arr.each_with_object (Hash.new (0)) {| (что, всего), h | h [what] + = total} '. Независимо от того, создает ли пустой хэш со значением по умолчанию, равным нулю (как я есть), или использует (чуть более эффективный) 'аккумулятор [what] || = 0', я предпочитаю' each_with_object' 'вводить' (aka' reduce') здесь, так как первое не требует, чтобы накопленное значение возвращалось в счетчик (голой «аккумулятор» в коде Симоны). [Enumerable # each_with_object] (http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-each_with_object) был с нами с версии v1.9. –

+0

Спасибо @CarySwoveland. Этот метод имеет такое странное имя, что я его никогда не помню, поэтому я обычно прихожу к «инъекции». : D –

1

Вы можете использовать три Enumerable методы: group_by, map и reduce (ака inject):

arr.group_by(&:first).map { |k,v| [k, v.map(&:last).reduce(:+)] } 
    #=> [["BEER", 313],["FOOD", 167]] 

стадии:

h = arr.group_by(&:first) 
    # => {"BEER"=>[["BEER", 37], ["BEER", 95], ["BEER", 85], ["BEER", 60], 
    #    ["BEER", 36]], 
    #  "FOOD"=>[["FOOD", 31], ["FOOD", 86], ["FOOD", 50], ["FOOD", 0]]} 

map присвоит блочные переменные первого элемент (значение ключа pa л) из h:

k = "BEER" 
v = [["BEER", 37], ["BEER", 95], ["BEER", 85], ["BEER", 60], ["BEER", 36]] 

затем выполняет вычисление блока:

[k, v.map(&:last).reduce(:+)] 
    #=> ["BEER", [37, 95, 85, 60, 36].reduce(:+)] 
    # ["BEER", 313] 

Второй элемент h передается к блоку:

k = "FOOD" 
v = [["FOOD", 31], ["FOOD", 86], ["FOOD", 50], ["FOOD", 0]] 
[k, v.map(&:last).reduce(:+)] 
    #=> ["FOOD", [31, 86, 50, 0].reduce(:+)] 
    # ["FOOD", 167] 

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

arr.chunk(&:first).map { |k,v| [k, v.map(&:last).reduce(:+)] } 

1 Поскольку ключи хеширования Ruby v1.9 заказываются по их порядку размещения. Поэтому каждый может ссылаться на n-й элемент (пару-ключ-значение) хэша. До v1.9 не было понято, какие ключи хеша заказываются. Для этих версий приведенный выше код работает нормально, но мы не знаем, какой порядок пар ключ-значение передается map его блоку.

+0

Жду этого :-) –

Смежные вопросы