2014-05-13 4 views
2

문제를 여러 필드를 계산 :해시에

data = [{"name"=>"name1", "priority"=>"1", "owner"=>"test3"}, 
     {"name"=>"name1", "priority"=>"1", "owner"=>"test4"}, 
     {"name"=>"name2", "priority"=>"1", "owner"=>"test5"}, 
     {"name"=>"name2", "priority"=>"2", "owner"=>"test5"}, 
     {"name"=>"nae954me2", "priority"=>"2", "owner"=>"test5"}] 

을 나는 각 [ID 당 레코드 수를 (계산하려면 : 내가 샘플로 고려, 특정 키를 추출하여 해시를 계산해야 나는 같은 것 말도록 명) 및 우선 순위]에서 추출한 : 나는 다음과 같은 일을 해요

#{{"priority"=>"1", "id"=>"name1"}=>2, {"priority"=>"1", "id"=>"name2"}=>1, {"priority"=>"2", "id"=>"name2"}=>1} 

을하지만 난 그것을과 복잡함을하고있어 느낌이 있습니다

#!/usr/bin/env ruby 

data = [{"name"=>"name1", "priority"=>"1", "owner"=>"test3"}, 
     {"name"=>"name1", "priority"=>"1", "owner"=>"test4"}, 
     {"name"=>"name2", "priority"=>"1", "owner"=>"test5"}, 
     {"name"=>"name2", "priority"=>"2", "owner"=>"test5"}, 
     {"name"=>"nae954me2", "priority"=>"2", "owner"=>"test5"}] 

# (1) trash some keys, just because I don't need them 
data.each do |d| 
    d.delete 'owner' 
    # in the real data I have about 4 or 5 that I'm trashing 
    d['id'] = d['name'].scan(/[a-z][a-z][a-z][a-z][0-9]/)[0] # only valid ids 
    d.delete 'name' 
end 

puts data 
#output: 
#{"priority"=>"1", "id"=>"name1"} 
#{"priority"=>"1", "id"=>"name1"} 
#{"priority"=>"1", "id"=>"name2"} 
#{"priority"=>"2", "id"=>"name2"} 
#{"priority"=>"2", "id"=>nil} 

# (2) reject invalid keys 
data = data.reject { |d| d['id'].nil? } 

puts data 
#output: 
#{"priority"=>"1", "id"=>"name1"} 
#{"priority"=>"1", "id"=>"name1"} 
#{"priority"=>"1", "id"=>"name2"} 
#{"priority"=>"2", "id"=>"name2"} 

# (3) count 
counts = Hash.new(0) 
data.each do |d| 
    counts[d] += 1 
end 

puts counts 
#{{"priority"=>"1", "id"=>"name1"}=>2, {"priority"=>"1", "id"=>"name2"}=>1, {"priority"=>"2", "id"=>"name2"}=>1} 
012,351을

카운팅 방법 개선에 대한 제안 사항이 있으십니까?

답변

1

이렇게하는 방법에는 여러 가지가 있습니다. (당신은 내가 나의 대답에 대해 많은 편집을했음을 눈치 챘을 것입니다. 방법이 어떻게 작동 하는지를 설명하고, 더 나은 방법이 있다는 것을 깨닫기 만하면됩니다.) 다음은 두 가지 해결책입니다. 첫 번째 방법은 여러분이 취한 접근에서 영감을 얻었지만 더 많은 Ruby와 비슷한 패키지로 만들려고 노력했습니다. 유효한 "이름"이 무엇인지 확신 할 수 없기 때문에 쉽게 변경할 수있는 별도의 방법으로 결정을 내 렸습니다.

코드

def name_valid?(name) 
    name[0..3] == "name" 
end 

data.each_with_object(Hash.new(0)) {|h,g| 
    (g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1) if name_valid?(h["name"])} 
    #=> {{"id"=>"name1", "priority"=>"1"}=>2, 
    # {"id"=>"name2", "priority"=>"1"}=>1, 
    # {"id"=>"name2", "priority"=>"2"}=>1} 

설명

Enumerable에서 #의 each_with_object 블록 변수 g로 표시되는 기본값 제로 초기 상태로 비어 해시를 생성합니다. gdata의 요소로부터 생성 된 해시 원소를 첨가하여 빌드 : 해시 g

{"id"=>h["name"],"priority"=>h["priority"]} 

하나씩 증가 된 키와 관련된 값을 갖고

g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1 

경우. h이 키가없는 경우,

g[{"id"=>h["name"],"priority"=>h["priority"]}] 

g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1 

가 호출되기 전에 0과 동일하게 설정 때문에 값이 1을하게된다.

다른 방법

코드 설명 여기

, 내가 Hash#update가 (일명 Hash#merge!가) data (해시)의 각 요소를 병합 사용했습니다

data.each_with_object({}) do |h,g| 
    hash = { { "id"=>h["name"], "priority"=>h["priority"] } => 1 } 
    g.update(hash) { |k, vg, _| vg + 1 } if name_valid?(h["name"]) 
end 
    #=> {{"id"=>"name1", "priority"=>"1"}=>2, 
    # {"id"=>"name2", "priority"=>"1"}=>1, 
    # {"id"=>"name2", "priority"=>"2"}=>1} 

초기에 비어있는 해시에 h ("name"의 값이 유효한 경우).update의 블록

{ |k, vg, _| vg + 1 } 

가 호출되면 병합 된 해시 (g)과 합류 해시 (hash)이 블록 키의 값을 리턴하는 경우 동일한 키 k를 가질 경우에만. 세 번째 블록 변수는 해시 hash에 대한 키 k의 값입니다. 우리가 그 값을 사용하지 않기 때문에 이것을 자리 표시 자 _으로 바꿨습니다.

1

는 "같은"무슨 뜻인지에 따라이 트릭 할 수 있습니다

data.group_by { |h| [h["name"], h["priority"]] }.map { |k, v| { k => v.size } } 

=> [{["name1", "1"]=>2}, {["name2", "1"]=>1}, {["name2", "2"]=>1}, {["nae954me2", "2"]=>1}] 
+0

영업 이익은 또한 유효하지 않은 이름을 필터링 것 같다,하지만 그래, 나는 GROUP_BY 갈 방법이라고 생각합니다. – Ron

+0

'group_by'를 사용하는 것은 좋은 접근 방법이지만, nemo는 결과가 "id"와 "priority"키를 가진 배열이 아닌 해시가되도록 요구했습니다. –