2016-10-04 3 views
-2

Так сказать, у меня есть 2D-массив. Может быть что-то вроде:Простейший способ суммирования элементов 2D-массива на основе дубликатов?

data = [['oct 1', 4], ['oct 2', 5], ['oct 3', 9], ['oct 1', 2]] 

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

Так что я бы в конечном итоге с:

data = (['oct 1', 6], ['oct 2', 5], ['oct 3', 9]) 

Я могу придумать несколько способов сделать это, но они, кажется, довольно inefficent и, как правило, есть всегда какой-то сумасшедший рубиновый код, который может сделать что-нибудь в несколько строк, какие-либо предложения?

+0

Мы хотели бы видеть вашу попытку решить эту проблему. Без этих доказательств усилий похоже, что вы хотите, чтобы мы выполнили работу, которую вы должны были сделать. Пожалуйста, прочитайте «[ask]», «[mcve]» и «[Сколько ожидаемых усилий для пользователей Stack Overflow?] (Http://meta.stackoverflow.com/a/261593/128421)». –

ответ

2

Вот несколько способов сделать это, мои предпочтения быть первым.

Используйте подсчета хэш

код

def combine(data) 
    data.each_with_object(Hash.new(0)) { |(date, val), h| h[date] += val }.to_a 
end 

См Hash::new, обсуждение значения по умолчанию, в частности.

Пример

data = [['oct 1', 4], ['oct 2', 5], ['oct 3', 9], ['oct 1', 2]] 
combine data 
    #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]] 

Объяснения

enum = data.each_with_object(Hash.new(0)) 
    #=> #<Enumerator: [["oct 1", 4], ["oct 2", 5], ["oct 3", 9], 
    #     ["oct 1", 2]]:each_with_object({})> 

Мы можем увидеть значение, генерируемое этого счетчику путем преобразования его в массив.

enum.to_a 
    #=> [[["oct 1", 4], {}], [["oct 2", 5], {}], [["oct 3", 9], {}], 
    # [["oct 1", 2], {}]] 

Первое значение enum передается в блок и значение блоков переменных вычисляются, используя устранения неоднозначности и параллельного назначение.

(date, val), h = enum.next 
    #=> [["oct 1", 4], {}] 
date 
    #=> "oct 1"  
val 
    #=> 4 

h[date] += val 
    #=> h[date] = h[date] + val 
    #=> h["oct 1"] = h["oct 1"] + 4 
    #=> h["oct 1"] = 0 + 4 (no key "oct 1" so default value of `0` used) 
    #=> h["oct 1"] = 4 

Теперь

h #=> {"oct 1"=>4} 

Остальные три значения enum передаются в блок и блок вычисления выполняются.

(date, val), h = enum.next 
    #=> [["oct 2", 5], {"oct 1"=>4}] 
h[date] += val 
    #=> 5 (the default value of `0` is again used) 
h #=> {"oct 1"=>4, "oct 2"=>5} 

(date, val), h = enum.next 
    #=> [["oct 3", 9], {"oct 1"=>4, "oct 2"=>5}] 
h[date] += val 
    #=> 9 (the default value of `0` is again used) 
h #=> {"oct 1"=>4, "oct 2"=>5, "oct 3"=>9} 

(date, val), h = enum.next 
    #=> [["oct 1", 2], {"oct 1"=>4, "oct 2"=>5, "oct 3"=>9}] 
h[date] += val 
    #=> 6 
h #=> {"oct 1"=>6, "oct 2"=>5, "oct 3"=>9} 

В последнем вычислении значение по умолчанию не используется, потому что хэш h уже имел ключ "oct 1":

h[date] += val 
    #=> h[date] = h[date] + val 
    #=> h["oct 1"] = h["oct 1"] + 2 
    #=> h["oct 1"] = 4 + 2 

Наконец,

h.to_a 
    #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]] 

Использование Enumerable#group_by

Код

def combine(data) 
    data.group_by(&:first).map { |date, vals| [date, vals.map(&:last).reduce(:+)] } 
end 

Пример

combine data 
    #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]] 

Пояснение

шаги:

h = data.group_by(&:first) 
    #=> {"oct 1"=>[["oct 1", 4], ["oct 1", 2]], 
    # "oct 2"=>[["oct 2", 5]], "oct 3"=>[["oct 3", 9]]} 

Th E первый ключ-значение пара h передается к блоку:

date, vals = h.first 
    #=> ["oct 1", [["oct 1", 4], ["oct 1", 2]]] 
date 
    #=> "oct 1" 
vals 
    #=> [["oct 1", 4], ["oct 1", 2]] 

и расчет блока выполняется.

a = vals.map(&:last) 
    #=> [4, 2] 
t = a.reduce(:+) 
    #=> 6 

Таким образом, первый ключ-значение пары h сопоставляется

[date, t] 
    #=> ["oct 1", 6] 

Остальные расчеты аналогичны.

+0

Спасибо, отличный ответ. Что касается моего форматирования кода, я новичок в этом сайте, поэтому я думал, что псевдо-реализация будет в порядке, мой плохой! – LukeCage

+0

Любые советы о том, как обобщить это на, например, 4D-массив? – LukeCage

+0

Это будет зависеть от того, как будет выглядеть массив. Например, будет ли тот же ключ присутствовать на разных уровнях? Лучше всего разместить отдельный вопрос (на примере). –

1

Try следуя с inject методом:

data = [["oct 1", 4], ["oct 2", 5], ["oct 3", 9], ["oct 1", 2]] 
data.inject({}) { |sum, (n, t)| sum[n] ||= 0; sum[n] += t; sum }.to_a 
=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]] 
Смежные вопросы