रूबी: हैश कीज़ को फ़िल्टर करने का सबसे आसान तरीका?


225

मेरे पास एक हैश है जो कुछ इस तरह दिखता है:

params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }

और मैं उन सभी कुंजियों को अस्वीकार करना चाहता हूं, जो पसंद + इंट नहीं हैं। यह पसंद 10 के माध्यम से पसंद 1 या पसंद 1 हो सकता है। ये बदलता रहता है।

मैं केवल शब्द विकल्प और उनके बाद एक अंक या अंकों के साथ कुंजियों को कैसे एकल करता हूं?

बक्शीश:

एक सीमांकक के रूप में हैश को टैब (\ t) के साथ एक स्ट्रिंग में बदलें। मैंने ऐसा किया था, लेकिन इसमें कोड की कई लाइनें थीं। आमतौर पर मास्टर्स रुबियंस इसे एक या एक ही लाइन में कर सकते हैं।


बोनस प्रश्न के संदर्भ में, क्या आप स्पष्ट कर सकते हैं कि आप स्ट्रिंग को क्या पसंद करेंगे।
mikej

निश्चित रूप से, उपर्युक्त उदाहरण हैश निकलेगा: "ओह देखो, एक और \ tEven अधिक तार \ tBut प्रतीक्षा करें" (स्ट्रिंग के अंत में not \ t, केवल उन दोनों के बीच)
Derek

आपका उदाहरण स्ट्रिंग टिप्पणी में काट दिया गया है। आप अपने प्रश्न को संपादित करने के लिए एडिट लिंक का उपयोग कर सकते हैं और उसमें उदाहरण जोड़ सकते हैं। आप उस रूबी को भी पोस्ट कर सकते हैं जिसे आप अभी तक प्राप्त करना चाहते हैं इसका एक उदाहरण है।
माइकज

जवाबों:


281

मूल उत्तर को संपादित करें : भले ही यह उत्तर है (इस टिप्पणी के समय के अनुसार) चयनित उत्तर है, लेकिन इस उत्तर का मूल संस्करण पुराना है।

मैं यहां एक अपडेट जोड़ रहा हूं ताकि दूसरों को इस जवाब से बचने में मदद मिल सके जैसे मैंने किया था।

जैसा कि अन्य उत्तर में उल्लेख किया गया है, रूबी> = 2.5 ने वह Hash#sliceविधि जोड़ी जो पहले केवल रेल में उपलब्ध थी।

उदाहरण:

> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}

संपादन का अंत। मूल उत्तर क्या है, जो मुझे लगता है कि उपयोगी होगा यदि आप रूबी के बिना रूबी <2.5 पर हैं, हालांकि मुझे लगता है कि इस बिंदु पर मामला बहुत असामान्य है।


यदि आप रूबी का उपयोग कर रहे हैं, तो आप selectविधि का उपयोग कर सकते हैं । आपको regexp मैच करने के लिए एक सिंबल से स्ट्रिंग में कुंजी बदलने की आवश्यकता होगी। यह आपको इसमें नए विकल्प के साथ एक नया हैश देगा।

choices = params.select { |key, value| key.to_s.match(/^choice\d+/) }

या आप delete_ifमौजूदा हैश उदाहरण के लिए उपयोग और संशोधित कर सकते हैं

params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }

या यदि यह सिर्फ चाबियाँ हैं और आपके द्वारा वांछित मान नहीं हैं तो आप कर सकते हैं:

params.keys.select { |key| key.to_s.match(/^choice\d+/) }

और यह कुंजी का एक एरियर देगा जैसे [:choice1, :choice2, :choice3]


1
अति उत्कृष्ट! एक बार जब मैं इसे देखता हूं, यह इतना आसान है! बहुत बहुत धन्यवाद, और दूसरों को भी धन्यवाद जिन्होंने जवाब देने के लिए समय लिया।
डेरेक

2
प्रतीकों को आजकल नियमित रूप से IIRC के साथ जोड़ा जा सकता है।
एंड्रयू ग्रिम

@ और, मुझे लगता है कि तुम सही हो। मैं 1.8.7 पर था जब मैं इस प्रश्न के उत्तर का परीक्षण कर रहा था।
मायकेज

1
प्रतिपक्ष .selectहै .reject, अगर यह आपके कोड को अधिक मुहावरेदार बनाता है।
जो एत्ज़बर्गर

जिस #sliceतरह से 2.5.3 पर मेरे लिए एक्टिविस्पोर्ट के बिना भी काम करता है।
ग्रेवुल्फ

305

रूबी में, हैश # चयन एक सही विकल्प है। यदि आप रेल के साथ काम करते हैं, तो आप हैश # स्लाइस और हैश # स्लाइस का उपयोग कर सकते हैं। उदाहरण (रेल 3.2.13)

h1 = {:a => 1, :b => 2, :c => 3, :d => 4}

h1.slice(:a, :b)         # return {:a=>1, :b=>2}, but h1 is not changed

h2 = h1.slice!(:a, :b)   # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4}

12
कठोर कुंजी के लिए बाहर देखो ..h1 = {'ए => 1:: बी => 2} केवल वापस आ जाएगी {: बी => 2} साथ h1.slice (: ए,: बी)
davidcollom

बहुत बढ़िया समाधान। मैंने इसका उपयोग रिस्पेक में रिटर्निंग रिजल्ट में किया, जहां मैं एक हैश के भीतर एक हैश के मूल्य को पार्स करना चाहता था। `` `परीक्षण = {: a => 1,: b => 2, c => {: A => 1,: B => 2}} समाधान ==> test.c.slice (: B)` ` `
प्रियंका

); ActiveSupport द्वारा रेल बदलें और इस एकदम सही है
Geoffroy

5
हालांकि यह सवाल का जवाब नहीं देता है, क्योंकि वह "पसंद + इंट" के लिए मानों को निकालने का एक तरीका चाहता है। आपका समाधान केवल समय से पहले ज्ञात कुंजी निकाल सकता है।
मेटकांगफू

46

सबसे आसान तरीका है gem 'activesupport'(या gem 'active_support') को शामिल करना ।

फिर, आपकी कक्षा में आपको केवल आवश्यकता है

require 'active_support/core_ext/hash/slice'

और कॉल करने के लिए

params.slice(:choice1, :choice2, :choice3) # => {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

मेरा मानना ​​है कि यह अन्य कार्यों की घोषणा करने के लायक नहीं है जिनमें बग हो सकते हैं, और यह एक ऐसी विधि का उपयोग करना बेहतर है जिसे कुछ वर्षों के दौरान ट्विक किया गया है।


कम से कम 2.5 पर इसके लिए Activerecord की आवश्यकता नहीं है।
ग्रेवुल्फ

13

सबसे आसान तरीका है कि मणि को 'एक्टीवसुपोर्ट' (या मणि 'active_support') शामिल किया जाए।

params.slice(:choice1, :choice2, :choice3)


4
कृपया अपने उत्तर में और विवरण जोड़ें।
मोहित जैन

13

यदि आप रेल के साथ काम करते हैं और आपके पास एक अलग सूची में चाबियाँ हैं, तो आप *नोटेशन का उपयोग कर सकते हैं :

keys = [:foo, :bar]
hash1 = {foo: 1, bar:2, baz: 3}
hash2 = hash1.slice(*keys)
=> {foo: 1, bar:2}

जैसा कि अन्य उत्तर में कहा गया है, आप भी slice!हैश को संशोधित करने के लिए उपयोग कर सकते हैं (और मिटाए गए कुंजी / मानों को वापस कर सकते हैं)।


9

पूर्ण मूल प्रश्न को हल करने के लिए यह एक पंक्ति है:

params.select { |k,_| k[/choice/]}.values.join('\t')

लेकिन ऊपर दिए गए अधिकांश समाधान एक ऐसे मामले को हल कर रहे हैं, जहां आपको समय से पहले कुंजियों को जानने की जरूरत है, sliceसरल रीजैक्सपी का उपयोग करके ।

यहां एक और दृष्टिकोण है जो सरल और अधिक जटिल उपयोग के मामलों के लिए काम करता है, जो कि रनटाइम पर स्वैपेबल है

data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)

अब न केवल कुंजियों या मूल्यों के मिलान पर अधिक जटिल तर्क के लिए अनुमति देता है, बल्कि परीक्षण करना भी आसान है, और आप रनटाइम पर मिलान तर्क को स्वैप कर सकते हैं।

मूल मुद्दे को हल करने के लिए पूर्व:

def some_method(hash, matcher) 
  hash.select(&matcher).values.join('\t')
end

params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }

some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait"
some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string"

1
मैं वास्तव में मिलान की तरह
Calin


6

यदि आप शेष हैश चाहते हैं:

params.delete_if {|k, v| ! k.match(/choice[0-9]+/)}

या यदि आप सिर्फ चाबियाँ चाहते हैं:

params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}

आपके पास कितने चॉइस और irrelevantX हैं, इस पर निर्भर करता है कि आपके पास सेलेक्ट करें या डिलीट करें। यदि आपके पास अधिक चॉइस है तो delete_if बेहतर है।
ayckoster

6

इसे इनिशियलाइज़र में डालें

class Hash
  def filter(*args)
    return nil if args.try(:empty?)
    if args.size == 1
      args[0] = args[0].to_s if args[0].is_a?(Symbol)
      self.select {|key| key.to_s.match(args.first) }
    else
      self.select {|key| args.include?(key)}
    end
  end
end

तब आप कर सकते हैं

{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # =>  {a: "1", b: "b"}

या

{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/)  # =>  {a: "1"}


4

बोनस प्रश्न के रूप में:

  1. यदि आपके पास #selectइस तरह की विधि से आउटपुट है (2-तत्व सरणियों की सूची):

    [[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "But wait"]]

    तो बस इस परिणाम को ले लो और निष्पादित करें:

    filtered_params.join("\t")
    # or if you want only values instead of pairs key-value
    filtered_params.map(&:last).join("\t")
  2. यदि आपके पास #delete_ifइस तरह की विधि (हैश) से आउटपुट है :

    {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

    फिर:

    filtered_params.to_a.join("\t")
    # or
    filtered_params.values.join("\t")

महान समाधान। एक प्रश्न: क्या फ़िल्टर्ड_परम्स.मैप (&: लास्ट) .जॉइन ("\ t") फ़िल्टर्ड_परम्स.मैप (! I | i.last) के लिए छोटा है। जॉइन ("\ t")। यदि हां, तो 'अंतिम' क्या है? क्या मैं हैश का मूल्य पाने के लिए &: का उपयोग कर सकता हूं?
डेरेक

mapयह तर्क के रूप में ब्लॉक लेता है। आप &:methodनिर्माण के साथ मक्खी पर ब्लॉक बना सकते हैं , जो ब्लॉक का निर्माण करेगा {|v| v.method }। मेरे मामले &:lastमें सरणी (2-तत्व सरणी) तर्क पर बुलाया गया था। यदि आप इसके साथ खेलना चाहते हैं, तो पहले जांच लें कि आप किस तर्क को ब्लॉक में प्राप्त कर रहे हैं (1 तर्क प्राप्त करें, इसे कई तर्कों में डिकॉन्स्ट्रक्ट नहीं करें!) और 1 विधि खोजने की कोशिश करें (आप के साथ श्रृंखला विधियों को नहीं कर सकते हैं &:method) जो आपको वापस लौटा देगा। चाहते हैं। यदि यह संभव है, तो आप शॉर्टकट का उपयोग कर सकते हैं, यदि नहीं, तो आपको पूर्ण ब्लॉक की आवश्यकता है
एमबीओ

2
params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }

choices = params.select { |key, value| key.to_s[/^choice\d+/] }
#=> {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

0

मेरे पास एक समान समस्या थी, मेरे मामले में समाधान एक लाइनर था जो कुंजी के प्रतीक नहीं होने पर भी काम करता है, लेकिन आपको एक सरणी में मानदंड की कुंजी होनी चाहिए

criteria_array = [:choice1, :choice2]

params.select { |k,v| criteria_array.include?(k) } #=> { :choice1 => "Oh look another one",
                                                         :choice2 => "Even more strings" }

एक और उदाहरण

criteria_array = [1, 2, 3]

params = { 1 => "A String",
           17 => "Oh look, another one",
           25 => "Even more strings",
           49 => "But wait",
           105 => "The last string" }

params.select { |k,v| criteria_array.include?(k) } #=> { 1 => "A String"}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.