कैसे एक हैश में चाबियाँ और मूल्यों स्वैप करने के लिए


154

मैं एक हाश में कुंजियों और मूल्यों को कैसे स्वैप करूं?

मेरे पास निम्नलिखित हैश है:

{:a=>:one, :b=>:two, :c=>:three}

में बदलना चाहता हूँ:

{:one=>:a, :two=>:b, :three=>:c}

प्रयोग mapथकाऊ लगता है। क्या कोई छोटा उपाय है?

जवाबों:


280

रूबी के पास हैश के लिए एक सहायक विधि है जो आपको एक हैश का इलाज करने देती है जैसे कि यह उलटा था (संक्षेप में, आपको मूल्यों के माध्यम से कुंजी तक पहुंचने की अनुमति देकर):

{a: 1, b: 2, c: 3}.key(1)
=> :a

यदि आप उल्टे हैश रखना चाहते हैं, तो हैश # इनवर्टर को अधिकांश स्थितियों के लिए काम करना चाहिए:

{a: 1, b: 2, c: 3}.invert
=> {1=>:a, 2=>:b, 3=>:c}

परंतु...

यदि आपके पास डुप्लिकेट मान हैं, तो invertअपने मूल्यों की अंतिम घटना को छोड़ देंगे , लेकिन (क्योंकि यह निर्गमन के दौरान उस कुंजी के लिए नए मूल्य को बदलना होगा)। इसी तरह, keyकेवल पहला मैच लौटाएंगे:

{a: 1, b: 2, c: 2}.key(2)
=> :b

{a: 1, b: 2, c: 2}.invert
=> {1=>:a, 2=>:c}

इसलिए, यदि आपके मूल्य अद्वितीय हैं, तो आप उपयोग कर सकते हैं Hash#invert। यदि नहीं, तो आप सभी मानों को एक सरणी के रूप में रख सकते हैं, जैसे:

class Hash
  # like invert but not lossy
  # {"one"=>1,"two"=>2, "1"=>1, "2"=>2}.inverse => {1=>["one", "1"], 2=>["two", "2"]} 
  def safe_invert
    each_with_object({}) do |(key,value),out| 
      out[value] ||= []
      out[value] << key
    end
  end
end

नोट: परीक्षणों वाला यह कोड अब GitHub पर है

या:

class Hash
  def safe_invert
    self.each_with_object({}){|(k,v),o|(o[v]||=[])<<k}
  end
end

4
each_with_objectसे अधिक समझ में आता है inject
एंड्रयू मार्शल

इतना कि each_with_object({}){ |i,o|k,v = *i; o[v] ||=[]; o[v] << k}... अच्छा हो जाता है
निगेल थोरने

3
हे भगवान। मुझे नहीं पता था कि आप कर सकते हैं (कुंजी, मूल्य), बाहर | इतना भयानक, मुझे लगता है कि कुंजी और मूल्य के बजाय वहां आने वाली सरणी से नफरत है। बहुत बहुत धन्यवाद
इउरी जी।

63

तुम शर्त लगा लो एक है! रूबी में चीजों को करने का हमेशा एक छोटा तरीका है!

यह बहुत आसान है, बस उपयोग करें Hash#invert:

{a: :one, b: :two, c: :three}.invert
=> {:one=>:a, :two=>:b, :three=>:c}

Et voilà!


4
यदि आपके हैश में कई बार समान मान दिखाई देते हैं तो हैश # इनवर्ट काम नहीं करता है।
तिलो

2
files = {
  'Input.txt' => 'Randy',
  'Code.py' => 'Stan',
  'Output.txt' => 'Randy'
}

h = Hash.new{|h,k| h[k] = []} # Create hash that defaults unknown keys to empty an empty list
files.map {|k,v| h[v]<< k} #append each key to the list at a known value
puts h

यह डुप्लिकेट मानों को भी संभाल लेगा।


1
क्या आप इसका उत्तर बता सकते हैं कि प्रत्येक चरण में क्या हो रहा है?
सज्जाद मुर्तज़ा

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

कोड के साथ उत्तर देते समय यह स्पष्ट करना महत्वपूर्ण है कि कोड कैसे काम करता है और यह उपयुक्त समाधान क्यों है। लक्ष्य केवल शिक्षित करना है, न कि तात्कालिक समस्या को हल करना।
टिन मैन

1
# this doesn't looks quite as elegant as the other solutions here,
# but if you call inverse twice, it will preserve the elements of the original hash

# true inversion of Ruby Hash / preserves all elements in original hash
# e.g. hash.inverse.inverse ~ h

class Hash

  def inverse
    i = Hash.new
    self.each_pair{ |k,v|
      if (v.class == Array)
        v.each{ |x|
          i[x] = i.has_key?(x) ? [k,i[x]].flatten : k
        }
      else
        i[v] = i.has_key?(v) ? [k,i[v]].flatten : k
      end
    }
    return i
  end

end

Hash#inverse आपको देता है:

 h = {a: 1, b: 2, c: 2}
 h.inverse
  => {1=>:a, 2=>[:c, :b]}
 h.inverse.inverse
  => {:a=>1, :c=>2, :b=>2}  # order might not be preserved
 h.inverse.inverse == h
  => true                   # true-ish because order might change

जबकि अंतर्निहित invertविधि अभी टूटी हुई है:

 h.invert
  => {1=>:a, 2=>:c}    # FAIL
 h.invert.invert == h 
  => false             # FAIL

1

सरणी का उपयोग करना

input = {:key1=>"value1", :key2=>"value2", :key3=>"value3", :key4=>"value4", :key5=>"value5"}
output = Hash[input.to_a.map{|m| m.reverse}]

हैश का उपयोग करना

input = {:key1=>"value1", :key2=>"value2", :key3=>"value3", :key4=>"value4", :key5=>"value5"}
output = input.invert

1

यदि आपके पास एक हैश है जहाँ कुंजियाँ अद्वितीय हैं, तो आप हैश # इनवर्ट का उपयोग कर सकते हैं :

> {a: 1, b: 2, c: 3}.invert
=> {1=>:a, 2=>:b, 3=>:c} 

यदि आपके पास गैर-विशिष्ट कुंजियाँ हैं, तो यह काम नहीं करेगा, हालाँकि, जहाँ केवल अंतिम कुंजियाँ देखी जाएँगी:

> {a: 1, b: 2, c: 3, d: 3, e: 2, f: 1}.invert
=> {1=>:f, 2=>:e, 3=>:d}

यदि आपके पास गैर विशिष्ट कुंजियों वाला हैश है, तो आप ऐसा कर सकते हैं:

> hash={a: 1, b: 2, c: 3, d: 3, e: 2, f: 1}
> hash.each_with_object(Hash.new { |h,k| h[k]=[] }) {|(k,v), h| 
            h[v] << k
            }     
=> {1=>[:a, :f], 2=>[:b, :e], 3=>[:c, :d]}

यदि हैश के मूल्य पहले से ही सरणियाँ हैं, तो आप यह कर सकते हैं:

> hash={ "A" => [14, 15, 16], "B" => [17, 15], "C" => [35, 15] }
> hash.each_with_object(Hash.new { |h,k| h[k]=[] }) {|(k,v), h| 
            v.map {|t| h[t] << k}
            }   
=> {14=>["A"], 15=>["A", "B", "C"], 16=>["A"], 17=>["B"], 35=>["C"]}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.