रूबी - पहले से ही एक सरणी नहीं तो एक सरणी के लिए परिवर्तनशील रूप से परिवर्तित करें


120

एक सरणी, एक एकल तत्व, या शून्य को देखते हुए, एक सरणी प्राप्त करें - बाद के दो क्रमशः एक एकल तत्व सरणी और एक खाली सरणी है।

मुझे गलती से लगा कि रूबी इस तरह काम करेगी:

[1,2,3].to_a  #= [1,2,3]     # Already an array, so no change
1.to_a        #= [1]         # Creates an array and adds element
nil.to_a      #= []          # Creates empty array

लेकिन जो आपको वास्तव में मिलता है वह है:

[1,2,3].to_a  #= [1,2,3]         # Hooray
1.to_a        #= NoMethodError   # Do not want
nil.to_a      #= []              # Hooray

तो इसे हल करने के लिए, मुझे या तो एक अन्य विधि का उपयोग करने की आवश्यकता है, या मैं उन सभी वर्गों की to_a विधि को संशोधित करके मेटा प्रोग्राम कर सकता हूं जो मैं उपयोग करने का इरादा रखता हूं - जो मेरे लिए कोई विकल्प नहीं है।

तो एक विधि यह है:

result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))

समस्या यह है कि यह थोड़ा गड़बड़ है। क्या ऐसा करने का एक सुंदर तरीका है? (मैं इस समस्या को हल करने के लिए रूबी-ईश तरीका है तो चकित हो जाएगा)


इसके क्या अनुप्रयोग हैं? यहां तक ​​कि एक सरणी में परिवर्तित क्यों?

कॉलिंग में 'ActiveRecord' का कहना है कि user.postsया तो पदों की एक सरणी, एक एकल पद या शून्य वापस कर देंगे। इस के परिणामों पर काम करने वाले तरीकों को लिखते समय, यह मान लेना सबसे आसान है कि विधि एक सरणी लेगी, जिसमें शून्य, एक या कई तत्व हो सकते हैं। उदाहरण विधि:

current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}

2
user.postsएक भी पद कभी नहीं लौटाना चाहिए। कम से कम, मैंने इसे कभी नहीं देखा।
सर्जियो तुलेंत्सेव

1
मुझे लगता है कि आपके पहले दो कोड ब्लॉक का मतलब आपके ==बजाय =, सही है?
पैट्रिक ऑसिटी


3
Btw, वापस नहीं[1,2,3].to_a करता है ! यह लौट आता है । [[1,2,3]][1,2,3]
पैट्रिक ऑसिटी

जवाबों:


153

[*foo]या Array(foo)ज्यादातर समय काम करेगा, लेकिन हैश जैसे कुछ मामलों के लिए, यह इसे गड़बड़ कर देता है।

Array([1, 2, 3])    # => [1, 2, 3]
Array(1)            # => [1]
Array(nil)          # => []
Array({a: 1, b: 2}) # => [[:a, 1], [:b, 2]]

[*[1, 2, 3]]    # => [1, 2, 3]
[*1]            # => [1]
[*nil]          # => []
[*{a: 1, b: 2}] # => [[:a, 1], [:b, 2]]

एकमात्र तरीका है कि मैं एक हैश के लिए भी काम कर सकता है एक विधि को परिभाषित करने के लिए है।

class Object; def ensure_array; [self] end end
class Array; def ensure_array; to_a end end
class NilClass; def ensure_array; to_a end end

[1, 2, 3].ensure_array    # => [1, 2, 3]
1.ensure_array            # => [1]
nil.ensure_array          # => []
{a: 1, b: 2}.ensure_array # => [{a: 1, b: 2}]

2
इसके बजाय ensure_array, विस्तार करेंto_a
Dan Grahn

9
@स्क्रीनmutt यह उन तरीकों को प्रभावित करेगा जो मूल उपयोग पर निर्भर करते हैं to_a। उदाहरण के लिए, {a: 1, b: 2}.each ...अलग तरह से काम करेगा।
आरा

1
क्या आप इस वाक्यविन्यास की व्याख्या कर सकते हैं? रूबी के कई वर्षों में मैं इस प्रकार के आह्वान पर कभी नहीं आया था। एक वर्ग के नाम पर कोष्ठक क्या करते हैं? मुझे यह डॉक्स में नहीं मिल रहा है।
मस्ताबेला

1
@mastaBlasta Array (arg) to_ary पर कॉल करके एक नया सरणी बनाने की कोशिश करता है, फिर तर्क पर to_a। यह आधिकारिक रूबी डॉक्स में प्रलेखित है। मैंने इसके बारे में अवधी की "कॉन्फिडेंट रूबी" किताब से सीखा।
मम्बू

2
@ मैं कुछ समय के बाद अपना प्रश्न पोस्ट करने के बाद मुझे इसका उत्तर मिल गया। कठिन हिस्सा यह था कि इसका ऐरे वर्ग के साथ कोई लेना-देना नहीं है लेकिन यह कर्नेल मॉड्यूल पर एक विधि है। रूबी- doc.org/core-2.3.1// कर्नेल.html#method
i-

119

ActiveSupport (रेल) के साथ: Array.wrap

Array.wrap([1, 2, 3])     # => [1, 2, 3]
Array.wrap(1)             # => [1]
Array.wrap(nil)           # => []
Array.wrap({a: 1, b: 2})  # => [{:a=>1, :b=>2}]

यदि आप रेल का उपयोग नहीं कर रहे हैं, तो आप अपने स्वयं के तरीके को रेल स्रोत के समान परिभाषित कर सकते हैं ।

class Array
  def self.wrap(object)
    if object.nil?
      []
    elsif object.respond_to?(:to_ary)
      object.to_ary || [object]
    else
      [object]
    end
  end
end

12
class Array; singleton_class.send(:alias_method, :hug, :wrap); endअतिरिक्त क्यूटनेस के लिए।
राठबेड

21

उपयोग करने के लिए सबसे सरल उपाय है [foo].flatten(1)। अन्य प्रस्तावित समाधानों के विपरीत, यह (नेस्टेड) ​​ऐरे, हैश और nil: के लिए अच्छी तरह से काम करेगा

def wrap(foo)
  [foo].flatten(1)
end

wrap([1,2,3])         #= [1,2,3]
wrap([[1,2],[3,4]])   #= [[1,2],[3,4]]
wrap(1)               #= [1]
wrap(nil)             #= [nil]
wrap({key: 'value'})  #= [{key: 'value'}]

दुर्भाग्य से इस एक अन्य मुद्दों की तुलना में गंभीर प्रदर्शन मुद्दा है। Kernel#Arrayयानी Array()इन सब में सबसे तेज है। रूबी 2.5.1 तुलना: ऐरे (): 7936825.7 आई / एस। Array.wrap: 4199036.2 i / s - 1.89x धीमा। लपेटो: 644030.4 i / s - 12.32x धीमी
वासिफ हुसैन


13

ActiveSupport (रेल)

इसके लिए ActiveSupport की एक बहुत अच्छी विधि है। यह रेल से भरा हुआ है, इसलिए ऐसा करने का सबसे अच्छा तरीका है:

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil

स्प्लैट (रूबी 1.9+)

*अगर यह कर सकता है, तो एक अलग एरे-ए- एरेट को स्पैट ऑपरेटर ( )

*[1,2,3] #=> 1, 2, 3 (notice how this DOES not have braces)

बेशक, एक सरणी के बिना, यह अजीब चीजें करता है, और जिन वस्तुओं को आप "स्पैट" करते हैं, उन्हें सरणियों में रखा जाना चाहिए। यह कुछ अजीब है, लेकिन इसका मतलब है:

[*[1,2,3]] #=> [1, 2, 3]
[*5] #=> [5]
[*nil] #=> []
[*{meh: "meh"}] #=> [[:meh, "meh"], [:meh2, "lol"]]

यदि आपके पास ActiveSupport नहीं है, तो आप विधि को परिभाषित कर सकते हैं:

class Array
    def self.wrap(object)
        [*object]
    end
end

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> nil

हालाँकि, यदि आप बड़े सरणियों, और कम गैर-सरणी चीज़ों की योजना बनाते हैं, तो आप इसे बदलना चाह सकते हैं - उपरोक्त विधि बड़े सरणियों के साथ धीमी है, और आपके स्टैक को ओवरफ़्लो करने का कारण भी बन सकती है (omg so meta)। वैसे भी, आप इसके बजाय ऐसा करना चाह सकते हैं:

class Array
    def self.wrap(object)
        object.is_a? Array ? object : [*object]
    end
end

Array.wrap([1, 2, 3]) #=> [1, 2, 3]
Array.wrap(nil) #=> [nil]

मेरे पास टेनेरे ऑपरेटर के साथ और बिना कुछ बेंचमार्क भी हैं ।


बड़े सरणियों के लिए काम नहीं करेंगे। SystemStackError: stack level too deep1M तत्वों (रूबी 2.2.3) के लिए।
denis.peplin

@ denis.peplin लगता है जैसे आपको एक StackOverflow त्रुटि मिली: D - ईमानदारी से, मुझे यकीन नहीं है कि क्या हुआ। माफ़ करना।
बेन ऑबिन

मैंने हाल ही Hash#values_atमें 1M तर्कों (उपयोग splat) के साथ प्रयास किया , और यह उसी त्रुटि को फेंकता है।
डेविसप्लिस्गिन

@ denis.peplin क्या यह साथ काम करता है object.is_a? Array ? object : [*object]?
बेन ऑबिन

1
Array.wrap(nil)रिटर्न []नहीं nil: /
22

7

कैसा रहेगा

[].push(anything).flatten

2
हाँ, मुझे लगता है कि मैं अपने मामले में [कुछ भी] .flatten का उपयोग करके समाप्त हो गया ... लेकिन सामान्य मामले के लिए यह किसी भी नेस्टेड सरणी संरचनाओं को भी समतल कर देगा
xxjjnn

1
[].push(anything).flatten(1)काम करेगा! यह नेस्टेड ऐरे को समतल नहीं करता है!
xxjjnn

2

स्पष्ट बताने के जोखिम के साथ, और यह जानते हुए भी कि यह ग्रह और आस-पास के क्षेत्रों पर देखी जाने वाली सबसे स्वादिष्ट सिंथैटिक चीनी नहीं है, यह कोड ठीक वही करता है जो आप बताते हैं:

foo = foo.is_a?(Array) ? foo : foo.nil? ? [] : [foo]

1

आप ऑब्जेक्ट की सरणी विधि को अधिलेखित कर सकते हैं

class Object
    def to_a
        [self]
    end
end

सब कुछ ऑब्जेक्ट को विरासत में मिला है, इसलिए to_a को अब सूर्य के तहत सब कुछ के लिए परिभाषित किया जाएगा


3
ईश निंदा करने वाला बंदर! पश्चाताप तु!
xxjjnn

1

मैं सभी उत्तरों से गुजरा हूं और ज्यादातर रूबी 2+ में काम नहीं करता

लेकिन इलाडो में सबसे सुंदर समाधान है

ActiveSupport (रेल) के साथ: Array.wrap

Array.wrap ([1, 2, 3]) # => [1, 2, 3]

Array.wrap (1) # => [1]

Array.wrap (nil) # => []

Array.wrap ({a: 1, b: 2}) # => [{: a => 1,: b => 2}]

अफसोस की बात है लेकिन यह भी रूबी 2+ के लिए काम नहीं करता है क्योंकि आपको एक त्रुटि मिलेगी

undefined method `wrap' for Array:Class

तो यह तय करने के लिए कि आपको आवश्यकता है।

'active_support / deprecation' की आवश्यकता है

'active_support / core_ext / array / wra' की आवश्यकता होती है


0

चूंकि विधि #to_aपहले से ही दो मुख्य समस्याग्रस्त वर्गों ( Nilऔर Hash) के लिए मौजूद है , बस विस्तार करके बाकी के लिए एक विधि को परिभाषित करें Object:

class Object
    def to_a
        [self]
    end
end

और फिर आप आसानी से किसी भी वस्तु पर उस विधि को कॉल कर सकते हैं:

"Hello world".to_a
# => ["Hello world"]
123.to_a
# => [123]
{a:1, b:2}.to_a
# => [[:a, 1], [:b, 2]] 
nil.to_a
# => []

5
मुझे वास्तव में लगता है कि बंदर को एक मुख्य रूबी वर्ग, विशेष रूप से वस्तु, से बचना है। मैं ActiveSupport को एक पास दूंगा, हालांकि मुझे एक पाखंडी समझते हैं। @ सवा द्वारा ऊपर दिए गए समाधान इससे कहीं अधिक व्यवहार्य हैं।
pho3nixf1re
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.