2011-01-20 21 views
15

मुझे एपीआई कॉल के जवाब के रूप में ऑब्जेक्ट्स का प्रतिनिधित्व करने वाले हैंश की एक सरणी मिली है। मुझे कुछ हैंश से डेटा खींचने की ज़रूरत है, और एक विशेष कुंजी हैश ऑब्जेक्ट के लिए आईडी के रूप में कार्य करती है। मैं सरणी को हैश के रूप में कुंजी के साथ एक हैश में परिवर्तित करना चाहता हूं, और मान उस आईडी के साथ मूल हैश के रूप में परिवर्तित करना चाहता हूं।हैश-ऑफ-हैश को हैश-ऑफ़-हैश में कनवर्ट करें, हैश की विशेषता द्वारा अनुक्रमित

api_response = [ 
    { :id => 1, :foo => 'bar' }, 
    { :id => 2, :foo => 'another bar' }, 
    # .. 
] 

ideal_response = { 
    1 => { :id => 1, :foo => 'bar' }, 
    2 => { :id => 2, :foo => 'another bar' }, 
    # .. 
} 

दो तरीके मैं यह कर के बारे में सोच सकता है:

यहाँ मैं किस बारे में बात कर रहा हूँ है।

  1. को ideal_response (नीचे) डेटा प्रत्येक रिकॉर्ड मैं का उपयोग करने की जरूरत है के लिए
  2. उपयोग api_response.find { |x| x[:id] == i } मैप करें।
  3. एक विधि जिसे मैं अनजान हूं, संभवतः map का उपयोग करने का एक तरीका हैश बनाने के लिए।

मैपिंग की मेरी विधि:

keys = data.map { |x| x[:id] } 
mapped = Hash[*keys.zip(data).flatten] 

मैं मदद नहीं कर सकता लेकिन लगता है कि एक अधिक performant, ऐसा करने का tidier तरीका है की तरह। विकल्प 2 बहुत ही प्रभावशाली होता है जब बहुत कम संख्या में रिकॉर्ड्स एक्सेस किए जाने की आवश्यकता होती है। मानचित्रण यहां उत्कृष्टता प्राप्त करता है, लेकिन प्रतिक्रिया में बहुत सारे रिकॉर्ड होने पर यह टूटना शुरू हो जाता है। शुक्र है, मुझे 50-100 से अधिक रिकॉर्ड होने की उम्मीद नहीं है, इसलिए मैपिंग पर्याप्त है।

क्या रूबी में ऐसा करने का एक स्मार्ट, साफ, या अधिक प्रदर्शन करने वाला तरीका है?

उत्तर

18

रूबी < = 2,0

Hash[api_response.map { |r| [r[:id], r] }] 
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

हालांकि, Hash::[] बहुत बदसूरत है और हमेशा की तरह बाएँ से सही OOP प्रवाह टूट जाता है। यही कारण है कि Facets प्रस्तावित Enumerable#mash है:

require 'facets' 
api_response.mash { |r| [r[:id], r] } 
# {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}} 

यह बुनियादी अमूर्त (हैश को enumerables परिवर्तित) अफसोस बहुत पहले रूबी में शामिल किए जाने,, without luck कहा गया।

रूबी> = 2।1

Enumerable#mash के लिए [अद्यतन] फिर भी प्यार नहीं है, लेकिन अब हम Array#to_h है। आदर्श नहीं -because हम एक मध्यवर्ती array- लेकिन बेहतर कुछ नहीं से की जरूरत है:

# ruby 2.1 
api_response.map { |r| [r[:id], r] }.to_h 
0

कुछ की तरह:

ideal_response = api_response.group_by{|i| i[:id]} 
#=> {1=>[{:id=>1, :foo=>"bar"}], 2=>[{:id=>2, :foo=>"another bar"}]} 

यह Enumerable के group_by का उपयोग करता है, जो संग्रह पर काम करता है, जो कुछ भी महत्वपूर्ण मूल्य आप चाहते हैं के लिए मैच लौटने। चूंकि यह कुंजी-मूल्य हिट से मेल खाने की कई घटनाओं को ढूंढने की अपेक्षा करता है, इसलिए यह उन्हें सरणी में जोड़ता है, इसलिए आप हैश के सरणी के हैश के साथ समाप्त होते हैं। यदि आप चाहते थे तो आप आंतरिक सरणी को वापस छील सकते हैं, लेकिन अगर आपके दो हैश आईडी टकराए गए हैं तो सामग्री को ओवरराइट करने का जोखिम चला सकता है। group_by आंतरिक सरणी के साथ avoids।

एक विशेष तत्व को एक्सेस करना आसान है:

ideal_response[1][0]  #=> {:id=>1, :foo=>"bar"} 
ideal_response[1][0][:foo] #=> "bar" 

जिस तरह से आप प्रश्न के अंत में दिखाने के कर का एक और वैध तरीका है। दोनों उचित रूप से तेज़ और सुरुचिपूर्ण हैं।

+0

यह प्रदर्शन और स्वच्छता के मामले में मैपिंग से बेहतर है, लेकिन 'डेटा [आईडी] [0] 'या' डेटा [आईडी] के माध्यम से रिकॉर्ड तक पहुंच रहा है। पहला' थोड़ा बोझिल है। हालांकि, मैं इस बिंदु पर नाइटपिक कर रहा हूं। चलो देखते हैं कि कोई और अंगूठी में अपनी टोपी फेंकने की परवाह करता है। – coreyward

+0

यह एक डुप्लिकेट कुंजी में चलाने की संभावना का परिणाम है। 'group_by' अज्ञात डेटा के साथ सुरक्षा के लिए डिज़ाइन किया गया एक सामान्य दिनचर्या है। अनुप्रयोग-विशिष्ट ज्ञान होने के नाते, जैसे कि आपको कभी भी, कभी भी, एक डुप्लिकेट और आने वाली कुंजी टकराव और डेटा हानि का सामना करना पड़ता है, डेटा संरचना को एक स्तर से सरल बनाने की अनुमति देगा। –

0

इसके लिए मैं शायद सिर्फ जाना चाहते हैं:

ideal_response = api_response.each_with_object(Hash.new) { |o, h| h[o[:id]] = o } 

सुपर ब्लॉक में कई ब्रैकेट के साथ सुंदर नहीं है, लेकिन यह api_response के केवल एक ही पुनरावृत्ति के साथ चाल है।

संबंधित मुद्दे