रूबी में अवरोही क्रम में एक सरणी को कैसे सॉर्ट करना है


282

मेरे पास हैश की एक सरणी है:

[
  { :foo => 'foo', :bar => 2 },
  { :foo => 'foo', :bar => 3 },
  { :foo => 'foo', :bar => 5 },
]

मैं इस सरणी :barको प्रत्येक हैश में मान के अनुसार अवरोही क्रम में क्रमबद्ध करने की कोशिश कर रहा हूं ।

मैं sort_byसरणी से ऊपर सॉर्ट करने के लिए उपयोग कर रहा हूं :

a.sort_by { |h| h[:bar] }

हालाँकि, यह आरोही क्रम में सरणी को सॉर्ट करता है। मैं इसे अवरोही क्रम में कैसे बना सकता हूं?

एक समाधान निम्नलिखित करना था:

a.sort_by { |h| -h[:bar] }

लेकिन वह नकारात्मक संकेत उचित नहीं लगता।


4
अन्य विकल्पों को देखते हुए मुझे अभी भी -h [: बार] सबसे सुरुचिपूर्ण लगता है। आपको इसके बारे में क्या पसंद नहीं है?
माइकल कोहल

2
मुझे कोड के इरादे को बताने में बहुत अधिक दिलचस्पी थी।
वसीम

1
@Waseem क्या मुझे स्वीकार किए गए उत्तर को अपडेट करने में परेशानी हो सकती है?
कोलिन

7
@ वर्तमान जवाब में कुछ भी गलत नहीं है। वहाँ सिर्फ एक बेहतर जवाब होना होता है। टिन मैन का उत्तर बहुत अधिक गहन है और दिखाता है कि sort_by.reverseवर्तमान में स्वीकृत उत्तर की तुलना में नाटकीय रूप से अधिक कुशल है। मेरा मानना ​​है कि यह "कोड के इरादे को व्यक्त करने" के लिए आपके द्वारा बताई गई चिंता को भी बेहतर ढंग से संबोधित करता है। शीर्ष पर, माणिक के वर्तमान संस्करण के लिए टिन मैन ने अपना उत्तर अपडेट किया है। इस सवाल को 15k बार देखा गया है। यदि आप प्रत्येक दर्शक के समय का 1 सेकंड भी बचा सकते हैं, तो मुझे लगता है कि यह इसके लायक है।
कोलिन

3
@ कोलिंडो धन्यवाद मैंने किया। :)
वसीम

जवाबों:


566

यह हमेशा विभिन्न सुझाए गए उत्तरों पर एक बेंचमार्क करने के लिए ज्ञानवर्धक है। यहाँ मुझे पता चला है:

#! / Usr / bin / माणिक

'बेंचमार्क' की आवश्यकता

ऐरी = []
1000. शर्तें { 
  ऐरी << {: बार => रैंड (1000)} 
}

n = 500
बेंचमार्क.बीएम (20) करना | x |
  x.report ("सॉर्ट") {n.times {ary.sort {| a, b | b [: बार] <=> ए [: बार]}}}
  x.report ("सॉर्ट रिवर्स") {n.times {ary.sort {| a, b | [a: बार] <=> b [: bar]} .reverse}}
  x.report ("Sort_by -a [: bar]") {n.times {ary.sort_by {| a | -एक बार] } } }
  x.report ("Sort_by a [: bar] * - 1") {n.times {ary.s.s_by {{a | [[: बार] * - १}}}
  x.report ("Sort_by.reverse!") {n.times {ary.sort_by {| a [[: बार]} .reverse}}
समाप्त

                          उपयोगकर्ता प्रणाली कुल वास्तविक
सॉर्ट 3.960000 0.010000 3.970000 (3.990886)
सॉर्ट रिवर्स 4.040000 0.000000 4.040000 (4.038849)
Sort_by -a [: बार] 0.690000 0.000000 0.690000 (0.692080)
Sort_by एक [: बार] * - 1 0.700000 0.000000 0.700000 (0.699735)
sort_by.reverse! 0.650000 0.000000 0.650000 (0.654447)

मुझे लगता है कि यह दिलचस्प है कि @ पाब्लो sort_by{...}.reverse!सबसे तेज है। परीक्षण चलाने से पहले मैंने सोचा था कि यह " -a[:bar]" की तुलना में धीमा होगा, लेकिन मूल्य को नकारने से अधिक समय लगता है जब यह एक सरणी में पूरे सरणी को उलट देता है। यह बहुत अंतर नहीं है, लेकिन हर छोटी गति में मदद करता है।


कृपया ध्यान दें कि ये परिणाम रूबी 1.9 में भिन्न हैं

यहाँ रूबी 1.9.3p194 (2012-04-20 संशोधन 35410) के परिणाम हैं [x86_64-darwin10.8.0]:

                           user     system      total        real
sort                   1.340000   0.010000   1.350000 (  1.346331)
sort reverse           1.300000   0.000000   1.300000 (  1.310446)
sort_by -a[:bar]       0.430000   0.000000   0.430000 (  0.429606)
sort_by a[:bar]*-1     0.420000   0.000000   0.420000 (  0.414383)
sort_by.reverse!       0.400000   0.000000   0.400000 (  0.401275)

ये एक पुराने मैकबुक प्रो पर हैं। नई या तेज मशीनों में कम मूल्य होंगे, लेकिन सापेक्ष अंतर बने रहेंगे।


यहाँ नए हार्डवेयर पर थोड़ा अद्यतन संस्करण और रूबी का 2.1.1 संस्करण है:

#!/usr/bin/ruby

require 'benchmark'

puts "Running Ruby #{RUBY_VERSION}"

ary = []
1000.times {
  ary << {:bar => rand(1000)}
}

n = 500

puts "n=#{n}"
Benchmark.bm(20) do |x|
  x.report("sort")               { n.times { ary.dup.sort{ |a,b| b[:bar] <=> a[:bar] } } }
  x.report("sort reverse")       { n.times { ary.dup.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse } }
  x.report("sort_by -a[:bar]")   { n.times { ary.dup.sort_by{ |a| -a[:bar] } } }
  x.report("sort_by a[:bar]*-1") { n.times { ary.dup.sort_by{ |a| a[:bar]*-1 } } }
  x.report("sort_by.reverse")    { n.times { ary.dup.sort_by{ |a| a[:bar] }.reverse } }
  x.report("sort_by.reverse!")   { n.times { ary.dup.sort_by{ |a| a[:bar] }.reverse! } }
end

# >> Running Ruby 2.1.1
# >> n=500
# >>                            user     system      total        real
# >> sort                   0.670000   0.000000   0.670000 (  0.667754)
# >> sort reverse           0.650000   0.000000   0.650000 (  0.655582)
# >> sort_by -a[:bar]       0.260000   0.010000   0.270000 (  0.255919)
# >> sort_by a[:bar]*-1     0.250000   0.000000   0.250000 (  0.258924)
# >> sort_by.reverse        0.250000   0.000000   0.250000 (  0.245179)
# >> sort_by.reverse!       0.240000   0.000000   0.240000 (  0.242340)

एक और हाल ही में मैकबुक प्रो पर रूबी 2.2.1 का उपयोग करके उपरोक्त कोड चलाने वाले नए परिणाम। फिर, सटीक संख्या महत्वपूर्ण नहीं हैं, यह उनके रिश्ते हैं:

Running Ruby 2.2.1
n=500
                           user     system      total        real
sort                   0.650000   0.000000   0.650000 (  0.653191)
sort reverse           0.650000   0.000000   0.650000 (  0.648761)
sort_by -a[:bar]       0.240000   0.010000   0.250000 (  0.245193)
sort_by a[:bar]*-1     0.240000   0.000000   0.240000 (  0.240541)
sort_by.reverse        0.230000   0.000000   0.230000 (  0.228571)
sort_by.reverse!       0.230000   0.000000   0.230000 (  0.230040)

रूबी 2.7.1 के लिए मध्य-2015 मैकबुक प्रो पर अपडेट किया गया:

Running Ruby 2.7.1
n=500     
                           user     system      total        real
sort                   0.494707   0.003662   0.498369 (  0.501064)
sort reverse           0.480181   0.005186   0.485367 (  0.487972)
sort_by -a[:bar]       0.121521   0.003781   0.125302 (  0.126557)
sort_by a[:bar]*-1     0.115097   0.003931   0.119028 (  0.122991)
sort_by.reverse        0.110459   0.003414   0.113873 (  0.114443)
sort_by.reverse!       0.108997   0.001631   0.110628 (  0.111532)

... रिवर्स विधि वास्तव में एक उलट सरणी नहीं लौटाती है - यह एक एन्यूमरेटर देता है जो बस अंत में शुरू होता है और पीछे की ओर काम करता है।

के लिए स्रोत Array#reverseहै:

               static VALUE
rb_ary_reverse_m(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    VALUE dup = rb_ary_new2(len);

    if (len > 0) {
        const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
        VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
        do *p2-- = *p1++; while (--len > 0);
    }
    ARY_SET_LEN(dup, RARRAY_LEN(ary));
    return dup;
}

do *p2-- = *p1++; while (--len > 0); अगर मैं अपने सी को सही ढंग से याद करता हूं तो पॉइंटर्स को तत्वों को उल्टे क्रम में कॉपी कर रहा है, इसलिए सरणी उलट है।


45
सुपर उपयोगी है। अतिरिक्त प्रयास के लिए धन्यवाद।
जोशुआ पिंटर

7
मैं इसे प्यार करता हूँ जब लोग इस तरह बेंचमार्क सबूत प्रदान करते हैं !! बहुत बढ़िया!
ktec

25
"मुझे अच्छा लगता है जब लोग इस तरह बेंचमार्क प्रूफ प्रदान करते हैं !!" मैं भी करता हूं, क्योंकि तब मेरे पास नहीं है।
टिन मैन

9
@theTinMan क्या आप अपने उत्तर के लिए कृपया एक TL; DR प्रदान कर सकते हैं। यह सभी बेंचमार्क जानकारी काफी उपयोगी है। लेकिन उत्तर के शीर्ष पर एक TL; DR उन लोगों के लिए उपयोगी होगा जो केवल उत्तर चाहते हैं। मुझे पता है कि उन्हें पूरी व्याख्या पढ़नी चाहिए और मुझे लगता है कि वे करेंगे। फिर भी एक TL, DR काफी उपयोगी IMHO होगा। आपके प्रयास के लिए धन्यवाद।
वसीम

8
मैं @Waseem से सहमत हूं। जैसा कि इस उत्तर के रूप में अच्छी तरह से शोध किया गया है, ओपी ने यह नहीं पूछा कि "रूबी में अवरोही क्रम करने का सबसे तेज़ तरीका क्या है"। बेंचमार्क के बाद सरल उपयोग दिखाने वाले शीर्ष पर एक टीएल; डीआर, इस उत्तर आईएमओ में सुधार करेगा।

89

बस एक त्वरित बात, जो अवरोही क्रम के इरादे को दर्शाता है।

descending = -1
a.sort_by { |h| h[:bar] * descending }

(मतलब समय में बेहतर तरीके से सोचेंगे);)


a.sort_by { |h| h[:bar] }.reverse!

पाब्लो, एक बेहतर तरीका खोजने पर अच्छा काम! मैंने जो बेंचमार्क देखा था।
टिन मैन

पहला तरीका तेज़ है (हालाँकि शायद बदसूरत) क्योंकि यह केवल एक बार ही चलता है। दूसरे के लिए, आपको इसकी आवश्यकता नहीं है!, जो कि inplace के संचालन के लिए है।
टोकनलैंड

3
यदि आप रिवर्स के बाद धमाके का उपयोग नहीं करते हैं, तो आप सरणी को उलट नहीं करेंगे बल्कि एक और बना सकते हैं जो उलट है।
पाब्लो फर्नांडीज

56

तुम यह कर सकते थे:

a.sort{|a,b| b[:bar] <=> a[:bar]}

4
लेकिन उपयोग करने की पूरी बात sort_byयह थी कि यह तुलनात्मक कार्य को कई बार चलाने से
बचता है

3
-1। sort_byइतना अधिक कुशल है, और अधिक पठनीय है। मूल्य को नकारना या अंत में एक रिवर्स करना तेजी से और अधिक पठनीय होगा।
मार्क-आंद्रे लाफोर्ट ने

1
मुझे यह उत्तर पसंद है क्योंकि * -1सभी मानों (जैसे समय) के साथ काम नहीं करता है, और reverseमानों को फिर से क्रमबद्ध करेगा जो समान के रूप में क्रमबद्ध हैं
Abe Voelker

8

मैं देखता हूं कि हमारे पास (दूसरों के बगल में) मूल रूप से दो विकल्प हैं:

a.sort_by { |h| -h[:bar] }

तथा

a.sort_by { |h| h[:bar] }.reverse

जब दोनों तरीके आपको समान परिणाम देते हैं जब आपकी सॉर्टिंग कुंजी अद्वितीय होती है, तो ध्यान रखें कि जिस reverseतरह से कुंजी के क्रम को उलट दिया जाएगा ।

उदाहरण:

a = [{foo: 1, bar: 1},{foo: 2,bar: 1}]
a.sort_by {|h| -h[:bar]}
 => [{:foo=>1, :bar=>1}, {:foo=>2, :bar=>1}]
a.sort_by {|h| h[:bar]}.reverse
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]

जबकि आपको अक्सर इस बारे में परवाह करने की आवश्यकता नहीं होती है, कभी-कभी आप करते हैं। इस तरह के व्यवहार से बचने के लिए आप एक दूसरी छँटाई कुंजी पेश कर सकते हैं (यह सुनिश्चित करने के लिए कि सभी वस्तुओं के लिए कम से कम अद्वितीय होने की आवश्यकता है जो एक ही छँटाई कुंजी हो):

a.sort_by {|h| [-h[:bar],-h[:foo]]}
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]
a.sort_by {|h| [h[:bar],h[:foo]]}.reverse
 => [{:foo=>2, :bar=>1}, {:foo=>1, :bar=>1}]

+1 इंगित करने के लिए कि शब्दार्थ reverseभिन्न हैं। मेरा मानना ​​है कि यह पिछले प्रकार को भी गड़बड़ करेगा, इस मामले में जहां कोई किसी क्रम में कई प्रकारों को लागू करने की कोशिश कर रहा है।
जौनीपाल

6

व्हाट अबाउट:

 a.sort {|x,y| y[:bar]<=>x[:bar]}

यह काम करता हैं!!

irb
>> a = [
?>   { :foo => 'foo', :bar => 2 },
?>   { :foo => 'foo', :bar => 3 },
?>   { :foo => 'foo', :bar => 5 },
?> ]
=> [{:bar=>2, :foo=>"foo"}, {:bar=>3, :foo=>"foo"}, {:bar=>5, :foo=>"foo"}]

>>  a.sort {|x,y| y[:bar]<=>x[:bar]}
=> [{:bar=>5, :foo=>"foo"}, {:bar=>3, :foo=>"foo"}, {:bar=>2, :foo=>"foo"}]

हाँ, यह वास्तव में काम करता है, लेकिन मुझे लगता है कि पीओ कोड के साथ इरादा दिखाना चाहता है (उसके पास पहले से ही काम करने वाला समाधान है)।
पाब्लो फर्नांडीज

जबकि sortइच्छा काम है, यह केवल तेजी से जब तत्काल मान छँटाई है। यदि आप उनके लिए खुदाई करने के लिए sort_byतेजी से है। बेंचमार्क देखें।
टिन मैन

3

उल्लिखित बेंचमार्क सूट के बारे में, ये परिणाम सॉर्ट किए गए सरणियों के लिए भी हैं।

sort_by/ reverseयह है:

# foo.rb
require 'benchmark'

NUM_RUNS = 1000

# arr = []
arr1 = 3000.times.map { { num: rand(1000) } }
arr2 = 3000.times.map { |n| { num: n } }.reverse

Benchmark.bm(20) do |x|
  { 'randomized'     => arr1,
    'sorted'         => arr2 }.each do |label, arr|
    puts '---------------------------------------------------'
    puts label

    x.report('sort_by / reverse') {
      NUM_RUNS.times { arr.sort_by { |h| h[:num] }.reverse }
    }
    x.report('sort_by -') {
      NUM_RUNS.times { arr.sort_by { |h| -h[:num] } }
    }
  end
end

और परिणाम:

$: ruby foo.rb
                           user     system      total        real
---------------------------------------------------
randomized
sort_by / reverse      1.680000   0.010000   1.690000 (  1.682051)
sort_by -              1.830000   0.000000   1.830000 (  1.830359)
---------------------------------------------------
sorted
sort_by / reverse      0.400000   0.000000   0.400000 (  0.402990)
sort_by -              0.500000   0.000000   0.500000 (  0.499350)

आपको Sort_by {} करना चाहिए। (धमाके के बिना रिवर्स एक नया सरणी बनाता है और मुझे उम्मीद है कि मैं निश्चित रूप से धीमा
रहूंगा

2

चढ़ते और उतरते समय से सरल समाधान है:

STRINGS

str = ['ravi', 'aravind', 'joker', 'poker']
asc_string = str.sort # => ["aravind", "joker", "poker", "ravi"]
asc_string.reverse # => ["ravi", "poker", "joker", "aravind"]

अंक

digit = [234,45,1,5,78,45,34,9]
asc_digit = digit.sort # => [1, 5, 9, 34, 45, 45, 78, 234]
asc_digit.reverse # => [234, 78, 45, 45, 34, 9, 5, 1]

1

उन लोगों के लिए जो IPS में गति मापना पसंद करते हैं;)

require 'benchmark/ips'

ary = []
1000.times { 
  ary << {:bar => rand(1000)} 
}

Benchmark.ips do |x|
  x.report("sort")               { ary.sort{ |a,b| b[:bar] <=> a[:bar] } }
  x.report("sort reverse")       { ary.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse }
  x.report("sort_by -a[:bar]")   { ary.sort_by{ |a| -a[:bar] } }
  x.report("sort_by a[:bar]*-1") { ary.sort_by{ |a| a[:bar]*-1 } }
  x.report("sort_by.reverse!")   { ary.sort_by{ |a| a[:bar] }.reverse }
  x.compare!
end

और परिणाम:

Warming up --------------------------------------
                sort    93.000  i/100ms
        sort reverse    91.000  i/100ms
    sort_by -a[:bar]   382.000  i/100ms
  sort_by a[:bar]*-1   398.000  i/100ms
    sort_by.reverse!   397.000  i/100ms
Calculating -------------------------------------
                sort    938.530   1.8%) i/s -      4.743k in   5.055290s
        sort reverse    901.157   6.1%) i/s -      4.550k in   5.075351s
    sort_by -a[:bar]      3.814k  4.4%) i/s -     19.100k in   5.019260s
  sort_by a[:bar]*-1      3.732k  4.3%) i/s -     18.706k in   5.021720s
    sort_by.reverse!      3.928k  3.6%) i/s -     19.850k in   5.060202s

Comparison:
    sort_by.reverse!:     3927.8 i/s
    sort_by -a[:bar]:     3813.9 i/s - same-ish: difference falls within error
  sort_by a[:bar]*-1:     3732.3 i/s - same-ish: difference falls within error
                sort:      938.5 i/s - 4.19x  slower
        sort reverse:      901.2 i/s - 4.36x  slower
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.