2011-11-28 15 views
27

एक वेब सेवा एक हैश लौट रही है जिसमें अज्ञात संख्या में नेस्टेड हैंश हैं, जिनमें से कुछ में एक अज्ञात है नेस्टेड हैश की संख्या।एक हैश के अंदर गहरी कुंजी/मूल्य जोड़े खोजें जिसमें नेस्टेड हैश और हैरे

कुछ चाबियाँ अद्वितीय नहीं हैं - यानी नेस्टेड हैंश में से एक से अधिक में मौजूद हैं।

हालांकि, सभी चाबियाँ जिन्हें मैं वास्तव में परवाह करता हूं वे सभी अद्वितीय हैं।

क्या कोई तरीका है कि मैं शीर्ष-स्तर हैश के लिए एक कुंजी दे सकता हूं, और इस मूल्य में वापस मूल्य प्राप्त करने के बावजूद यह मूल्य वापस प्राप्त कर सकता है?

(वेब ​​सेवा अमेज़न उत्पाद विज्ञापन एपीआई, जो थोड़ा परिणामों की संरचना है कि यह परिणामों की संख्या और खोज प्रकार प्रत्येक उत्पाद श्रेणी में अनुमति दी के आधार पर देता है भिन्न होता है।)

+1

यह प्रश्न बहुत कुछ आता है, जैसे [यहां] (http://stackoverflow.com/questions/1820451/ruby-style-how-to-check-whether-a-nested-hash-element-exists) और [यहां] (http://stackoverflow.com/questions/7139471/transform-a-ruby-hash-into-a-dotted-path-key-string) और कई अन्य। –

+1

यह हमेशा मदद करता है अगर आप कुछ नमूना डेटा बना सकते हैं जो आपको दिखा रहा है, तो हमें कल्पना करने की ज़रूरत नहीं है। साथ ही, डेटा कैसे भेजा जा रहा है? क्या आप एक्सएमएल प्राप्त करते हैं और इसे पार्स करते हैं? JSON? या, क्या आप एक कॉल का उपयोग कर रहे हैं जो रहस्यमय संरचना देता है और बाकी सब कुछ एक काला बॉक्स है? –

उत्तर

24

यहाँ एक सरल पुनरावर्ती है समाधान:

def nested_hash_value(obj,key) 
    if obj.respond_to?(:key?) && obj.key?(key) 
    obj[key] 
    elsif obj.respond_to?(:each) 
    r = nil 
    obj.find{ |*a| r=nested_hash_value(a.last,key) } 
    r 
    end 
end 

h = { foo:[1,2,[3,4],{a:{bar:42}}] } 
p nested_hash_value(h,:bar) 
#=> 42 
+6

इस कोड ने मुझे ढेर ओवरफ्लो का कारण बना दिया। मुझे लगता है कि यह स्ट्रिंग्स और/या कुछ और है जो 'प्रत्येक विधि 'का जवाब देगा। मैंने 'elsif obj.respond_to बदल दिया? (: प्रत्येक)' to 'elsif obj.is_a? (हैश) या obj.is_a? (ऐरे)'। अब यह ठीक काम करता है। आपके समाधान के लिए धन्यवाद। – Vigneshwaran

+2

यह अच्छा होगा अगर यह चीज़ अपने पथ (ब्रेडक्रंब?) को मुद्रित कर दे तो ... –

+0

क्या होगा यदि इसमें कई हैंश हैं: बार कुंजी, अगर हम प्रत्येक के मानों की सरणी चाहते हैं तो समाधान क्या होगा: बार कुंजी? – RSB

9

यह एक आम समस्या प्रतीत होने के बावजूद, मैं बस थोड़ी देर के खर्च किया है लगता है/साथ मैं वास्तव में क्या जरूरत है, जो मुझे लगता है कि आपकी आवश्यकता के रूप में ही है आने के लिए कोशिश कर रहा। पहली प्रतिक्रिया में लिंक में से कोई भी स्पॉट-ऑन नहीं है।

class Hash 
    def deep_find(key) 
    key?(key) ? self[key] : self.values.inject(nil) {|memo, v| memo ||= v.deep_find(key) if v.respond_to?(:deep_find) } 
    end 
end 

तो दिया:

hash = {:get_transaction_list_response => { :get_transaction_list_return => { :transaction => [ { ... 

निम्नलिखित: लेन-देन कुंजी:

hash.deep_find(:transaction) 

के साथ जुड़े सरणी मिल जाएगा।

यह इष्टतम नहीं है क्योंकि इंजेक्शन जारी रहेगा, भले ही ज्ञापन पॉप्युलेट हो।

23

ऊपर जवाब और टिप्पणियों के कुछ संयोजन:

class Hash 
    def deep_find(key, object=self, found=nil) 
    if object.respond_to?(:key?) && object.key?(key) 
     return object[key] 
    elsif object.is_a? Enumerable 
     object.find { |*a| found = deep_find(key, a.last) } 
     return found 
    end 
    end 
end 
0

मैं निम्नलिखित कोड

def search_hash(hash, key) 
    return hash[key] if hash.assoc(key) 
    hash.delete_if{|key, value| value.class != Hash} 
    new_hash = Hash.new 
    hash.each_value {|values| new_hash.merge!(values)} 
    unless new_hash.empty? 
    search_hash(new_hash, key) 
    end 
end 
0

मैं एक छोटे से trie खोज मैंने लिखा के लिए इस का उपयोग कर समाप्त हो गया का उपयोग करें:

def trie_search(str, obj=self) 
    if str.length <= 1 
    obj[str] 
    else 
    str_array = str.chars 
    next_trie = obj[str_array.shift] 
    next_trie ? trie_search(str_array.join, next_trie) : nil 
    end 
end 

नोट: यह फिलहाल घोंसला वाले हैंश के लिए है। वर्तमान में कोई सरणी समर्थन नहीं है।

13

बंदर पैचिंग के लिए कोई ज़रूरत नहीं, बस Hashie मणि का उपयोग करें: https://github.com/intridea/hashie#deepfind

user = { 
    name: { first: 'Bob', last: 'Boberts' }, 
    groups: [ 
    { name: 'Rubyists' }, 
    { name: 'Open source enthusiasts' } 
    ] 
} 

user.extend Hashie::Extensions::DeepFind 

user.deep_find(:name) #=> { first: 'Bob', last: 'Boberts' } 

मनमाना Enumerable वस्तुओं के लिए, वहाँ एक और विस्तार उपलब्ध है DeepLocate: https://github.com/intridea/hashie#deeplocate

+0

यह क्यों चिह्नित किया गया? –

+1

मुझे हशी :: एक्सटेंशन :: दीपफिंड एक उत्कृष्ट दृष्टिकोण के रूप में मिला। और यदि आप डुप्लीकेट की गई कुंजियां ढूंढ रहे हैं, तो deep_find_all() विधि अद्भुत है। अत्यधिक सिफारिशित। – JESii

2

barelyknown के समाधान के एक बदलाव: यह मिलेगा पहले मैच की बजाय हैश में एक कुंजी के लिए सभी मान।

class Hash 
    def deep_find(key, object=self, found=[]) 
    if object.respond_to?(:key?) && object.key?(key) 
     found << object[key] 
    end 
    if object.is_a? Enumerable 
     found << object.collect { |*a| deep_find(key, a.last) } 
    end 
    found.flatten.compact 
    end 
end 

{a: [{b: 1}, {b: 2}]}.deep_find(:b) वापस आ जाएगी [1, 2]

0

रेल क्योंकि 5 ActionController :: पैरामीटर नहीं रह गया है हैश से विरासत, मैं विधि को संशोधित करने और यह मापदंडों के विशिष्ट बनाने के लिए किया है।

module ActionController 
    class Parameters 
    def deep_find(key, object=self, found=nil) 
     if object.respond_to?(:key?) && object.key?(key) 
     return object[key] 
     elsif object.respond_to?(:each) 
     object = object.to_unsafe_h if object.is_a?(ActionController::Parameters) 
     object.find { |*a| found = deep_find(key, a.last) } 
     return found 
     end 
    end 
    end 
end 

तो कुंजी पाया जाता है, यह है कि कुंजी का मान देता है, लेकिन यह एक ActionController :: पैरामीटर वस्तु इतनी मजबूत पैरामीटर संरक्षित नहीं किया जाता वापस नहीं करता है।

+0

एकाधिक पैरा कुंजी के लिए काम नहीं किया: {0: [{b: '1'}], 1: [{b: '2'}]} .deep_find (: b) रिटर्न: #> '1 ' – m1l05z

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