تعيين مجموعة تعديل العناصر الوحيدة مطابقة شرط معين
سؤال
في روبي، ما هي الطريقة الأكثر تعبيرا لرسم صفيف في مثل هذه الطريقة أن بعض العناصر يتم تعديل وترك الآخرين دون أن تمس م>
وهذا هو الطريق المستقيم إلى الأمام للقيام بذلك:
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