2017-10-01 1 views
0

발권 시스템에 히트 맵 (this과 같은 테이블)을 만들고 싶습니다. JSON 형식의 db 데이터에서 모든 티켓 세부 정보를 수신합니다. 아래는 그 예입니다. 실제 데이터에는 1000 개 이상의 레코드가 있습니다.루비에서 히트 맵을 생성하기위한 알고리즘

{"ticketCount": 6, 
"tickets": 
    [ 
    {"creationTimeMs": 1506061704724, 
    "expirationTimeMs": 1506083304724, 
    "queue": "low"}, 
    {"creationTimeMs": 1506127874782, 
    "expirationTimeMs": 1506149474782, 
    "queue": "low"}, 
    {"creationTimeMs": 1506283760321, 
    "expirationTimeMs": 1506283760322, 
    "queue": "high"}, 
    {"creationTimeMs": 1506236363281, 
    "expirationTimeMs": 1506257963281, 
    "queue": "high"}, 
    {"creationTimeMs": 1506283655948, 
    "expirationTimeMs": 1506283667938, 
    "queue": "low"}, 
    {"creationTimeMs": 1506283781894, 
    "expirationTimeMs": 1506284781894, 
    "queue": "medium"} 
    ] 
} 

I는 열과 행 및 남은 시간 (currentTime - expirationTime) 등 (고정되지 않은) 큐 이름 테이블 싶다. 나는 < 10 분, 10-30 분, 30-1hr, 1-5 시간,> 5 시간 만료와 5 열을 갖고 싶습니다.

저는 json을 반복해서 반복함으로써 무차별 대항하는 법을 알고 있습니다. 나는 우리에게 최상의 알고리즘과 루비가 단순하게 제공 할 수있는 것이 있는지 알고 싶었다.

+2

루비에서는 일반적으로 결과를 합계로 나누기 위해'group_by'와 다른 것들을 사용합니다. – tadman

답변

1

코드

require 'json' 

def cross_tab(json, range_mins) 
    JSON.parse(json)["tickets"].each_with_object(Hash.new(0)) do |g,h| 
    diff = g["etime"]-g["ctime"] 
    h[[g["queue"], range_mins.rindex { |mn| mn <= diff }]] += 1 
    end 
end 

json = '{"ticketCount": 6, 
    "tickets": [ 
    {"ctime": 1506061704724, "etime": 1506083304724, "queue": "low"}, 
    {"ctime": 1506127874782, "etime": 1506149474782, "queue": "low"}, 
    {"ctime": 1506283760321, "etime": 1506283760322, "queue": "high"}, 
    {"ctime": 1506236363281, "etime": 1506257963281, "queue": "high"}, 
    {"ctime": 1506283655948, "etime": 1506283667938, "queue": "low"}, 
    {"ctime": 1506283781894, "etime": 1506284781894, "queue": "medium"} 
    ] 
}' 

range_mins = [0, 10, 30, 60, 300].map { |n| 60000 * n } 
    #=> [0, 600_000, 1_800_000, 3_600_000, 18_000_000] 

h = cross_tab(json, range_mins) 
    #=> {["low", 4]=>2, ["high", 0]=>1, ["high", 4]=>1, ["low", 0]=>1, ["medium", 1]=>1} 

h[["high", 4]] 
    #=> 1 
h[["low", 3]] 
    #=> 0 

h0없이 키 ["low", 3] 기본값을 가지고 있기 때문에 두 번째 결과를 얻는다.

이제 우리는 다음과 같이 크로스 탭 (또는 크로스 집계 또는 비상 테이블)의 내용을 구성 할 수 있습니다.

row_map = { 0=>"low", 1=>"medium", 2=>"high" } 

tbl = Array.new(row_map.size) { |i| 
     Array.new(range_mins.size) { |j| h[[row_map[i], j]] } } 
    #=> [[1, 0, 0, 0, 2], 
    # [0, 1, 0, 0, 0], 
    # [1, 0, 0, 0, 1]] 

행은 (열) 레이블은 우리가 양자 택일 json에서 row_map을 계산할 수 row_map (range_mins)

에서 얻을 수 있습니다.

JSON.parse(json)["tickets"].map { |h| h["queue"] }.uniq. 
    map.with_index { |queue, i| [i, queue] }.to_h 
    #=> {0=>"low", 1=>"high", 2=>"medium"} 

그러나 이것은 우리가 테이블의 행의 순서를 지정하거나 "queue"의 값의 일부 테이블을 생성 할 수 없습니다.

설명

있어서 해시 대한 디폴트 값 (여기서는 0) 인수를 취하는 클래스 Hash::new 방법의 형태를 사용한다. 즉, h = Hash.new(0)hk 키가없는 경우 h[k]이 기본값을 반환합니다.

이렇게 정의 된 해시는 이라고도하며 으로 계산되며 일반적으로 사용되며 계산 방식이 h[k] +=1 인 경우 여기에서 사용됩니다.루비는이를 볼 때, 그녀가 제일 먼저 0, h 기본 값으로 변환됩니다 평등 (방법 Hash#[])의 오른쪽에 키 k, h[k]이없는 경우

h[k] = h[k] + 1 

로 확장하다 . 이 표현식이 동일한 키 k에 대해 실행될 때마다 오른쪽의 h[k]은 현재 값 k을 반환합니다 (즉, 기본값은 적용되지 않습니다). (왼쪽에있는 h[k]은 기본값과 아무 관계가없는 Hash#[]= 메서드입니다.)

다음 단계를 따르십시오. 블록에 전달

h = JSON.parse(json) 
    #=> {"ticketCount"=>6, 
    # "tickets"=>[ 
    #  {"ctime"=>1506061704724, "etime"=>1506083304724, "queue"=>"low"}, 
    #  {"ctime"=>1506127874782, "etime"=>1506149474782, "queue"=>"low"}, 
    #  {"ctime"=>1506283760321, "etime"=>1506283760322, "queue"=>"high"}, 
    #  {"ctime"=>1506236363281, "etime"=>1506257963281, "queue"=>"high"}, 
    #  {"ctime"=>1506283655948, "etime"=>1506283667938, "queue"=>"low"}, 
    #  {"ctime"=>1506283781894, "etime"=>1506284781894, "queue"=>"medium"} 
    # ] 
    # } 
a = h["tickets"] 
    #=> [{"ctime"=>1506061704724, "etime"=>1506083304724, "queue"=>"low"}, 
    # {"ctime"=>1506127874782, "etime"=>1506149474782, "queue"=>"low"}, 
    # {"ctime"=>1506283760321, "etime"=>1506283760322, "queue"=>"high"}, 
    # {"ctime"=>1506236363281, "etime"=>1506257963281, "queue"=>"high"}, 
    # {"ctime"=>1506283655948, "etime"=>1506283667938, "queue"=>"low"}, 
    # {"ctime"=>1506283781894, "etime"=>1506284781894, "queue"=>"medium"}] 
e = a.each_with_object(Hash.new(0)) 
    #=> #<Enumerator: [ 
    #  {"ctime"=>1506061704724, "etime"=>1506083304724, "queue"=>"low"}, 
    #  {"ctime"=>1506127874782, "etime"=>1506149474782, "queue"=>"low"}, 
    #  ... 
    #  {"ctime"=>1506283781894, "etime"=>1506284781894, "queue"=>"medium"} 
    # ]:each_with_object({})> 

제 1 엘리먼트 소자를 열거함으로써 생성

는, 상기 블록은 그 변수의 값과 동일하게 설정되며, 상기 블록 계산이 수행된다.

g, h = e.next 
    # => [{"ctime"=>1506061704724, "etime"=>1506083304724, "queue"=>"low"}, {}] 
g #=> {"ctime"=>1506061704724, "etime"=>1506083304724, "queue"=>"low"} 
h #=> {} 
f = g["queue"] 
    #=> "low" 
diff = g["etime"]-g["ctime"] 
    #=> 1506083304724 - 1506061704724 => 21600000 
j = range_mins.rindex { |mn| mn <= diff } 
    #=> 4 

range_mins[4] #=> 18_000_000diff (21_600_000)`이하인 range_mins의 최대 값 인 것을 나타내고있다. ,

k = [f, j] 
    #=> ["low", 4] 
h[k] += 1 
    #=> 1 
h #=> {["low", 4]=>1} 

다음 값을 계속하는 것은 다음 열거 e하여 블록으로 전달된다.

g, h = e.next 
    #=> [{"ctime"=>1506127874782, "etime"=>1506149474782, "queue"=>"low"}, 
    # {["low", 4]=>1}] 
g #=> {"ctime"=>1506127874782, "etime"=>1506149474782, "queue"=>"low"} 
h #=> {["low", 4]=>1} 
f = g["queue"] 
    #=> "low" 
diff = g["etime"]-g["ctime"] 
    #=> 1506149474782 - 1506127874782 => 21600000 
j = range_mins.rindex { |mn| mn <= diff } 
    #=> 4 
k = [f, j] 
    #=> ["low", 4] 
h[k] += 1 
    #=> 2 
h #=> {["low", 4]=>2} 

나머지 단계는 유사합니다.

0

group_by의 연속 응용 프로그램에서이를 수행 할 수 있습니다.

data['tickets'].group_by { |ticket| ticket['queue'] }.transform_values do |tickets| 
    tickets.group_by |ticket| 
    # categorize ticket by time until expiry 
    end 
end 

같은 뭔가이 첫 번째 레벨의 키는 큐 이름이고 두 번째 수준 키 당신이 만료 시간 가입시 선택하신 카테고리입니다 중첩 된 해시 결과.

관련 문제