تعيين مجموعة تعديل العناصر الوحيدة مطابقة شرط معين

StackOverflow https://stackoverflow.com/questions/614335

  •  03-07-2019
  •  | 
  •  

سؤال

في روبي، ما هي الطريقة الأكثر تعبيرا لرسم صفيف في مثل هذه الطريقة أن بعض العناصر يتم تعديل وترك الآخرين دون أن تمس

وهذا هو الطريق المستقيم إلى الأمام للقيام بذلك:

old_a = ["a", "b", "c"]                         # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) }  # ["a", "b!", "c"]

والاستغناء عن حالة "ترك وحده" بالطبع لم يكن كافيا:

new_a = old_a.map { |x| x+"!" if x=="b" }       # [nil, "b!", nil]

ما أود شيء من هذا القبيل:

new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"}) 
        do |y|
          y + "!"
        end
# ["a", "b!", "c"]

هل هناك طريقة لطيفة للقيام بذلك في روبي (أو ربما القضبان لديه بعض نوع من طريقة الراحة التي لم أجد حتى الآن)؟


وشكر الجميع للرد. بينما كنت على قناعة جماعية لي أنه من الأفضل لمجرد استخدام map مع المشغل الثلاثي، البعض منكم نشر إجابات مثيرة جدا للاهتمام!

هل كانت مفيدة؟

المحلول

وأوافق على أن البيان خريطة جيدة كما هو. انها واضحة وبسيطة ،، وسيكون من السهل على أي شخص للمحافظة عليه.

إذا كنت تريد شيئا أكثر تعقيدا، ماذا عن هذا؟

module Enumerable
  def enum_filter(&filter)
    FilteredEnumerator.new(self, &filter)
  end
  alias :on :enum_filter
  class FilteredEnumerator
    include Enumerable
    def initialize(enum, &filter)
      @enum, @filter = enum, filter
      if enum.respond_to?(:map!)
        def self.map!
          @enum.map! { |elt| @filter[elt] ? yield(elt) : elt }
        end
      end
    end
    def each
      @enum.each { |elt| yield(elt) if @filter[elt] }
    end
    def each_with_index
      @enum.each_with_index { |elt,index| yield(elt, index) if @filter[elt] } 
    end
    def map
      @enum.map { |elt| @filter[elt] ? yield(elt) : elt }
    end
    alias :and :enum_filter
    def or
      FilteredEnumerator.new(@enum) { |elt| @filter[elt] || yield(elt) }
    end
  end
end

%w{ a b c }.on { |x| x == 'b' }.map { |x| x + "!" } #=> [ 'a', 'b!', 'c' ]

require 'set'
Set.new(%w{ He likes dogs}).on { |x| x.length % 2 == 0 }.map! { |x| x.reverse } #=> #<Set: {"likes", "eH", "sgod"}>

('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy"

نصائح أخرى

ولأن صفائف مؤشرات، وهذا يعمل أيضا:

a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }

puts a.inspect
# => ["hello", "to!", "you!", "dude"]

في الحلقة، تأكد من استخدام الأسلوب الذي يغير الكائن بدلا من إنشاء كائن جديد. مثلا upcase! مقارنة upcase.

والإجراء المحدد يعتمد على بالضبط ما تحاول تحقيقه. انه من الصعب الظفر إجابة محددة مع أمثلة فو بار.

old_a.map! { |a| a == "b" ? a + "!" : a }

وتعطي

=> ["a", "b!", "c"]

وmap! يعدل المتلقي في المكان، حتى old_a الآن أن مجموعة إرجاعها.

وحل map الخاص بك هو أفضل واحد. لست متأكدا لماذا تعتقد map_modifying_only_elements_where هو أفضل نوعا ما. باستخدام map هو أنظف وأكثر إيجازا، و لا يتطلب كتل متعددة.

واحد بطانة:

["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }

في التعليمات البرمجية أعلاه، عليك أن تبدأ مع [] "التراكمي". كما يمكنك تعداد من خلال العداد (في حالتنا مجموعة، [ "أ"، "ب"، "ج"])، التراكمي، وكذلك الحصول على تمرير إلى كتلة لدينا "الحالي" البند (| تراكمية، ط |) و يتم تعيين نتيجة التنفيذ لدينا كتلة لتراكمية. ما أقوم به هو الحفاظ على أعلى تراكمية دون تغيير عند هذا البند هو لا "ب" وإلحاق "ب"! لمجموعة تراكمية وإعادته عندما يكون ب.

وهناك جوابا سبق أن تستخدم select، والذي هو أسهل طريقة للقيام (وتذكر) عليه.

ويمكنك الجمع بين select مع map من أجل تحقيق ما كنت تبحث عن:

 arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
 => ["b!"]

وداخل كتلة select، يمكنك تحديد شروط عنصر إلى أن "مختارة". هذا سيعود صفيف. يمكنك الاتصال "خريطة" على مجموعة مما أدى إلى إلحاق علامة تعجب لذلك.

إذا كنت لا تحتاج إلى مجموعة من العمر، وتفضل الخريطة! في هذه الحالة لأنه لا يمكن استخدام! طريقة لتمثيل كنت تقوم بتغيير مجموعة في المكان.

self.answers.map!{ |x| (x=="b" ? x+"!" : x) }

وأنا أفضل هذا على:

new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }

وانها بضعة أسطر طويلة، ولكن هنا بديلا لأنها من الجحيم:

oa = %w| a b c |
na = oa.partition { |a| a == 'b' }
na.first.collect! { |a| a+'!' }
na.flatten! #Add .sort! here if you wish
p na
# >> ["b!", "a", "c"]

وووجمع مع الثلاثي يبدو الأفضل في رأيي.

ولقد وجدت أن أفضل طريقة لتحقيق ذلك هو عن طريق استخدام tap

arr = [1,2,3,4,5,6]
[].tap do |a|
  arr.each { |x| a << x if x%2==0 }
end
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top