रूबी में दिए गए लम्बाई के टुकड़ों में एक स्ट्रिंग को काटने का सबसे अच्छा तरीका क्या है?


87

मैं रूबी में दिए गए लम्बाई के सब्सट्रिंग्स में एक स्ट्रिंग को चट करने के लिए एक सुरुचिपूर्ण और कुशल तरीके की तलाश में हूं।

अब तक, सबसे अच्छा मैं इस के साथ आ सकता है:

def chunk(string, size)
  (0..(string.length-1)/size).map{|i|string[i*size,size]}
end

>> chunk("abcdef",3)
=> ["abc", "def"]
>> chunk("abcde",3)
=> ["abc", "de"]
>> chunk("abc",3)
=> ["abc"]
>> chunk("ab",3)
=> ["ab"]
>> chunk("",3)
=> []

आप इसके बजाय chunk("", n)वापस लौटना चाह सकते हैं । यदि हां, तो इसे विधि की पहली पंक्ति के रूप में जोड़ें:[""][]

return [""] if string.empty?

क्या आप कोई बेहतर उपाय सुझाएंगे?

संपादित करें

इस सुरुचिपूर्ण और कुशल समाधान के लिए जेरेमी रुटेन को धन्यवाद: [संपादित करें: कुशल नहीं!]

def chunk(string, size)
    string.scan(/.{1,#{size}}/)
end

संपादित करें

मूल स्लाइस-आधारित समाधान जो केवल 2.4 सेकंड लेता है, की तुलना में स्ट्रिंग.scan समाधान को 60k को 1k chunks में 10000k में 60 सेकंड तक का समय लगता है।


आपका मूल समाधान जितना संभव हो उतना कुशल और सुरुचिपूर्ण है: यह जानने के लिए स्ट्रिंग के प्रत्येक चरित्र का निरीक्षण करने की कोई आवश्यकता नहीं है, और न ही किसी चीज़ को पूरी तरह से एक सरणी में बदलने और फिर वापस करने की आवश्यकता है।
android.weasel

जवाबों:


158

उपयोग करें String#scan:

>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx", "yz"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,3}/)
=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]

ठीक है, अब यह बहुत अच्छा है! मुझे पता था कि एक बेहतर तरीका होना चाहिए। बहुत बहुत धन्यवाद जेरेमी रुटेन।
मिनीक्वायर्क

3
def चंक (स्ट्रिंग, आकार); string.scan (/ {1, # {आकार}} /।); अंत
मिनीक्वार्क

1
वाह, मुझे अब बेवकूफ लग रहा है। मैंने कभी यह जांचने के लिए परेशान नहीं किया कि स्कैन कैसे काम करता है।
चक

18
इस समाधान के साथ सावधान रहें; यह एक regexp है, और इसका /.थोड़ा सा मतलब है कि इसमें सभी अक्षर EXCEPT शामिल होंगे \n। यदि आप newlines को शामिल करना चाहते हैं, तो उपयोग करेंstring.scan(/.{4}/m)
प्रोफेसेर्मेटिंगटन

1
क्या एक चतुर समाधान! मैं regexps प्यार करता हूँ, लेकिन मैं इस उद्देश्य के लिए मात्रा का उपयोग करने का नहीं होगा। धन्यवाद जेरेमी रुतैन
सीईसी

18

यहाँ यह करने का एक और तरीका है:

"abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s }

=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]


15
वैकल्पिक रूप से:"abcdefghijklmnopqrstuvwxyz".chars.each_slice(3).map(&:join)
फिनबर

3
मुझे यह पसंद है क्योंकि यह उन स्ट्रिंग्स पर काम करता है जिनमें न्यूलाइन्स होते हैं।
स्टीव डेविस

1
यह स्वीकृत समाधान होना चाहिए। यदि लंबाई पैटर्न से मेल नहीं खाती है, तो स्कैन का उपयोग अंतिम टोकन छोड़ सकता है ।
गिनती 0

6

मुझे लगता है कि यह सबसे कारगर उपाय है यदि आप जानते हैं कि आपका तार कई आकार का है

def chunk(string, size)
    (string.length / size).times.collect { |i| string[i * size, size] }
end

और भागों के लिए

def parts(string, count)
    size = string.length / count
    count.times.collect { |i| string[i * size, size] }
end

3
यदि आप के string.length / sizeसाथ प्रतिस्थापित करते हैं तो आपके तार को कई प्रकार के चंक आकार का होना जरूरी नहीं है (string.length + size - 1) / size- यह पैटर्न सी कोड में आम है जिसे पूर्णांक कटाव से निपटना पड़ता है।
नाइट्रोजन

3

यहां थोड़ा अलग मामले के लिए एक और एक समाधान है, जब बड़े तारों को संसाधित करते हैं और एक बार में सभी विखंडू को संग्रहीत करने की आवश्यकता नहीं होती है। इस तरह यह एक समय में एक ही हिस्सा संग्रहीत करता है और तारों को काटने की तुलना में बहुत तेज प्रदर्शन करता है:

io = StringIO.new(string)
until io.eof?
  chunk = io.read(chunk_size)
  do_something(chunk)
end

बहुत बड़ी तार के लिए, यह है अब तक यह करने के लिए सबसे अच्छा तरीका है । यह पूरे स्ट्रिंग को मेमोरी में पढ़ने और Errno::EINVALत्रुटियों को प्राप्त करने Invalid argument @ io_freadऔर जैसे होने से बचाएगा Invalid argument @ io_write
जोशुआ पिंटर

2

मैंने थोड़ा परीक्षण किया जो कि 593MB डेटा को 18991 32KB टुकड़ों में काटता है। आपके स्लाइस + मैप वर्जन को ctrl + C दबाने से पहले 100% CPU का उपयोग करके कम से कम 15 मिनट तक चला। स्ट्रिंग # अनपैक का उपयोग करके यह संस्करण 3.6 सेकंड में समाप्त हो गया:

def chunk(string, size)
  string.unpack("a#{size}" * (string.size/size.to_f).ceil)
end

1
test.split(/(...)/).reject {|v| v.empty?}

अस्वीकार करना आवश्यक है क्योंकि इसमें सेट के बीच रिक्त स्थान शामिल है। मेरा रेगेक्स-फू मेरे सिर के ऊपर से उस अधिकार को ठीक करने के तरीके को देखने के लिए काफी नहीं है।


स्कैन aproach गैर-मिलान किए गए बैक्टीरिया के बारे में भूल जाएगा, अर्थात: यदि आप 3 भागों पर 10 लंबाई के स्ट्रिंग स्लाइस के साथ प्रयास करते हैं, तो आपके पास 3 भाग होंगे और 1 तत्व गिरा दिया जाएगा, आपका aproach ऐसा नहीं करता है, इसलिए यह सबसे अच्छा है।
विनीसियस गाति

1

एक बेहतर समाधान जो स्ट्रिंग के अंतिम भाग को ध्यान में रखता है जो कि चंक आकार से कम हो सकता है:

def chunk(inStr, sz)  
  return [inStr] if inStr.length < sz  
  m = inStr.length % sz # this is the last part of the string
  partial = (inStr.length / sz).times.collect { |i| inStr[i * sz, sz] }
  partial << inStr[-m..-1] if (m % sz != 0) # add the last part 
  partial
end

0

क्या आपके मन में कुछ और अड़चनें हैं? वरना मैं कुछ आसान करने के लिए अजीब तरह से ललचाऊंगा

[0..10].each {
   str[(i*w),w]
}

मेरे पास वास्तव में कोई बाधा नहीं है, इसके अलावा कुछ सरल, सुरुचिपूर्ण और कुशल है। मुझे आपका विचार पसंद है, लेकिन क्या आप कृपया इसे एक विधि में तब्दील करना चाहेंगे? [०.१.१०] शायद थोड़ा और जटिल हो जाएगा।
मिनीक्वायार्क

मैंने अपने उदाहरण को str [i w, w] के बजाय str [i w ... (i + 1) * w] का उपयोग करने के लिए निर्धारित किया है । टीएक्स
मिनीक्वार्क

यह (१.१०) होना चाहिए। [०.१०]। [१.१०] एक सरणी है जिसमें एक तत्व होता है - एक सीमा। (१.१०) अपनी सीमा है। और प्रत्येक + उस ब्लॉक द्वारा लौटाए गए मूल्यों के बजाय मूल संग्रह (जिसे इस मामले में [१.१०]) पर लौटाता है। हम + नक्शा + चाहते हैं।
चक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.