2015-01-19 2 views
1

DB 호출의 해시가 하나 이상 (1000 일 수도 있음) 있습니다. 이 경우 두 가지가 있습니다.모든 배열이 함께 압축 된 키 배열의 해시 합치기

{ "water_need"=>[9959, 9959, 9959, 9959, 9959, 9959, 9959, 9959, 9959, 9959, 9959, 9959], "forecast_savings"=>[-2479, -2479, -2479, -2479, -2479, -2479, -2479, -2479, -2479, -2479, -2479, -2479], "water_actual"=>[7480, 7480, 7480, 7480, 7480, 7480, 7480, 7480, 7480, 7480, 7480, 7480] } 
{ "water_need"=>[21090, 21090, 21090, 21090, 21090, 21090, 21090, 21090, 21090, 21090, 21090, 21090], "forecast_savings"=>[-20890, -20890, -20890, -20890, -20890, -20890, -20890, -20890, -20890, -20890, -20890, -20890], "water_actual"=>[200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200] } 

각 해시는 동일한 키를 가지고 있습니다. 각 해시는 키 값과 동일한 길이 배열을가집니다. 어떤 배열에는 nil이있을 수 있습니다. 모든 값이 nil이면 배열은 여전히 ​​모두 nil의 12 길이 배열입니다.

목표는 해시와 배열이 동일한 값을 가진 동일한 키를 가진 단일 해시를 반환하는 것입니다. 예상 결과 :

def add_arrays(first, *others) 
    first.zip(*others).map { |column| column.reduce(&:+) } 
end 

내 생각은 모든 'water_needs' 예를 들어, 다음에 세트를 먹이를 보유하고 몇 가지 새로운 변수로 비슷한 키를 매핑하는 것입니다

{ "water_need"=>[31049, 31049, 31049, 31049, 31049, 31049, 31049, 31049, 31049, 31049, 31049, ], "forecast_savings"=>[-23369, -23369, -23369, -23369, -23369, -23369, -23369, -23369, -23369, -23369, -23369, -23369], "water_actual"=>[7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680] } 

나는 배열을 추가하려면이 방법을 발견 추가 된 값을 반환하는 add_arrays 메서드 나는 이것을하기위한 올바른 방법에 대한 지침을 얻기 위해 여기에왔다.

업데이트 :

나는 오후에이 작업을했습니다. 여기 내 솔루션은 지금까지이지만 제대로 NILS를 처리하지 않습니다

@month = [] 
    @water_need = [] 
    @cost_need = [] 
    @forecast_savings = [] 
    @forecast_cost_savings = [] 
    # @etc_padding = [] 
    @water_actual = [] 
    @reduction_amount = [] 

    @benchmark.each_with_index do |record, index| 
     @water_need[index]    = record['water_need'] 
     @cost_need[index]    = record['cost_need'] 
     @forecast_savings[index]  = record['forecast_savings'] 
     @forecast_cost_savings[index] = record['forecast_cost_savings'] 
     # @etc_padding[index]   = record['etc_padding'] 
     @water_actual[index]   = record['water_actual'] 
     @reduction_amount[index]  = record['reduction_amount'] 
    end 

    @water_need_total    = @water_need.transpose.map(&:sum) 
    @cost_need_total    = @cost_need.transpose.map(&:sum) 
    @forecast_savings_total  = @forecast_savings.transpose.map(&:sum) 
    @forecast_cost_savings_total = @forecast_cost_savings.transpose.map(&:sum) 
    # @etc_padding_total   = @etc_padding.transpose.map(&:sum) 
    @water_actual_total   = @water_actual.transpose.map(&:sum) 
    @reduction_amount_total  = @reduction_amount.transpose.map(&:sum) 

    @cumulative_baseline = { month: month_array, 
          water_need: @water_need_total, 
          cost_need: @cost_need_total, 
          forecast_savings: @forecast_savings_total, 
          forecast_cost_savings: @forecast_cost_savings_total, 
          # etc_padding: @etc_padding_total, 
          water_actual: @water_actual_total, 
          reduction_amount: @reduction_amount_total 
          } 

    ap @cumulative_baseline 

'AP 통신'입니다 궁금해 경우 누구의 최고 출력 (콘솔 형식에 대한 보석).

코드가 완성되지 않았습니다. 코드 작성 스타일이 먼 길을 먼저 쓰는 경향이 있습니다. 모든 것이 내 머리 속에서 분명 해졌고, 추상화를 찾아 내고 다시 꺼내 들었습니다. 이것은 코드의 현재 상태입니다. 최종 형식임을 나타내지는 않습니다.

@benchmark는 모든 해시가 포함 된 개체입니다. 그것은 일련의 레코드 (ActiveRecord 호출에 공통적 인)이고 레코드는 필자가 작성한 것처럼 키가있는 해시이며 (예 : water_need) 해당 키의 값은 해당 배열입니다.

+0

이 예에서는 단일 배열의 모든 값이 동일 함을 의미합니다. 그렇다면 데이터에 냄새가 있습니다. 그들은 과다하다. 그렇지 않은 경우, 귀하의 예는 오해의 소지가 있고 부적절합니다. – sawa

+0

배열 당 12 개의 요소를 나타내는 요소는 해당 연도에 12 개월을 나타냅니다. 때로는 숫자가 같거나 때로는 그렇지 않습니다. 그것들이 같거나 같지 않은 것은 동일한 '유형'의 다중 배열을 합하는 작업에 아무런 영향을 미치지 않습니다. – notaceo

답변

1
hashes = [ 
    {a: [1,2], b: [1, 2], c: [100, 200]}, 
    {a: [3,nil], b: [nil, nil], c: [300, 400]}, 
    {a: [5,4], b: [1, 2], c: [nil, nil]}, 
] 

result = hashes.reduce {|h, v| 
    h.merge(v) do |k, o, n| 
    o.zip(n).map {|a, b| a.to_i + b.to_i } 
    end 
} 

require 'pp' 
pp result 

출력

{:a=>[9, 6], :b=>[2, 4], :c=>[400, 600]} 

설명 두 해시를 병합하는 방법을 를 지정

mergetakes a block argument. 이 경우 병합하는 방법은 add_arrays과 같은 방식으로 배열을 함께 추가하는 것입니다. 처음 hashes.reduce은 배열의 첫 번째 요소와 함께 자동으로 시드됩니다.

+0

많은 배열 c, d, e, f 등이있을 수 있으므로 크기가 조정되지 않았습니다. – notaceo

+0

그것은': a'와': b'를 모두 포함하는 것으로 보여지는 것처럼 어떤 수의 배열에서도 작동합니다. 나는 또한': c',': d' 등을 포함 할 수 있으며 여전히 작동 할 것이다. 그게 무슨 생각을하고 있었습니까? –

+0

시연하기 위해 세 번째 배열': c'를 추가했습니다.FWIW 나는 병합이 당신이하려고하는 것의 _intent_을 표현하기 때문에이 문제를 다루기위한 올바른 방법이라고 생각한다 : 여러 것을 병합한다. –

0

다음과 같이 제안합니다.

코드

def totals_by_key(arr) 
    arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |g,h| 
     g.each { |k,v| h[k] << v } } 
    .tap { |h| h.each { |k,v| 
     h[k] = v.transpose.map { |c| c.compact.reduce(0,:+) } } } 
end 

arr = [{ "water_need"  =>[ 9959, 9959, 9959], 
     "forecast_savings" =>[ -2479, -2479, -2479], 
     "water_actual"  =>[ 7480, 7480, 7480] }, 
     { "water_need"  =>[ 21090, 21090, 21090], 
     "forecast_savings" =>[-20890, -20890, -20890], 
     "water_actual"  =>[ nil, nil, nil] }] 

totals_by_key(arr) 
    #=> {"water_need"   =>[ 31049, 31049, 31049], 
    # "forecast_savings" =>[-23369, -23369, -23369], 
    # "water_actual"  =>[ 7480, 7480, 7480]} 

설명내

f = arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |g,h| 
    g.each { |k,v| h[k] << v } } 
    #=> { "water_need"  =>[[ 9959, 9959, 9959], [ 21090, 21090, 21090]], 
    #  "forecast_savings" =>[[-2479, -2479, -2479], [-20890, -20890, -20890]], 
    #  "water_actual"  =>[[ 7480, 7480, 7480], [ nil, nil, nil]] } 

님의 블록입니다. each 그 블록에 h 각각의 키 - 값 쌍을 전달 제 존재 그래서 블록 변수 할당

"water_need" => [[ 9959, 9959, 9959], [ 21090, 21090, 21090]] 

다음 작업 후 수행

k = "water_need" 
v = [[ 9959, 9959, 9959], [ 21090, 21090, 21090]] 

:

t = v.transpose 
    #=> [[9959, 21090], [9959, 21090], [9959, 21090]] 
u = t.map { |c| c.compact.reduce(0,:+) } 
    #=> [31049, 31049, 31049] 

nil을 제거한 후 후자의 연산은 각 요소 (배열)의 값을 t의 합계로 계산합니다. map 그 블록에 제 존재를 t의 각 요소를 통과하여 수행 :

c = [9959, 21090] 

정도로 f

d = c.compact 
    #=> [9959, 21090] 
d.reduce(0,:+) 
    #=> 31049 

다른 두 키 - 값 쌍은 상기와 마찬가지로 처리된다. taph을 반환하며이 메서드에 의해 차례대로 반환됩니다.

참고하는 경우 :

a = [nil, nil, nil] 
b = a.compact 
[].reduce(0,:+) 
    #=> 0 

반면

[].reduce(:+) 
    #=> nil 

변종

대신 :

arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |g,h| 
    g.each { |k,v| h[k] << v } } 

당신이 승 수 rite :

arr.each_with_object({}) { |g,h| g.each { |k,v| (h[k] ||= []) << v } } 
+0

나는 이것을 내일 시험해 보려한다. - 처음 읽는 순간 너무 추상적이어서 선물이있다. :) – notaceo

+0

고마워, notaceo,하지만 선물이 아니야. 그것은 단지 언어에 대한 경험을 얻고 있습니다. 내가 루비를 처음 접했을 때 나는 어둠 속에서 걸림돌이 된 것처럼 느꼈다. 새로운 언어와 마찬가지로 처음에는 모든 단어를 번역해야합니다. 잠시 후 잠재 의식이 그 집안일 중 일부를 처리하여 단어를 결합 할 수있는 방법을 생각하게합니다. 그런 다음 패턴 인식을 시작합니다. 미적분을 배울 때를 생각해보십시오. 상당한 연습 후에 문제를 볼 수 있고 당신의 두뇌는 즉시 "부품에 의한 통합"을 외칠 것입니다. –

관련 문제