রুবিতে, এমন একটি অ্যারে পদ্ধতি রয়েছে যা 'নির্বাচন' এবং 'মানচিত্রের' সমন্বয় করে?


97

আমার কাছে একটি রুবি অ্যারে রয়েছে যার কয়েকটি স্ট্রিংয়ের মান রয়েছে। আমার দরকার:

  1. কিছু প্রাকটিকের সাথে মেলে এমন সমস্ত উপাদান সন্ধান করুন
  2. একটি রূপান্তর মাধ্যমে ম্যাচিং উপাদান চালান
  3. ফলাফলগুলি অ্যারে হিসাবে ফিরিয়ে দিন

এখনই আমার সমাধানটি এর মতো দেখাচ্ছে:

def example
  matchingLines = @lines.select{ |line| ... }
  results = matchingLines.map{ |line| ... }
  return results.uniq.sort
end

এমন কোন অ্যারে বা গণনীয় পদ্ধতি আছে যা নির্বাচন এবং মানচিত্রকে একক যৌক্তিক বিবৃতিতে সম্মিলিত করে?


5
এই মুহুর্তে কোনও পদ্ধতি নেই, তবে রুবিকে একটি যুক্ত করার প্রস্তাব রয়েছে: বাগস.রবি -আলংআর.ইউএসইউস
স্টেফানক্লাব

Enumerable#grepপদ্ধতি ঠিক কি জিজ্ঞাসা করা হল এবং উপর দশ বছরের জন্য রুবি হয়েছে না। এটি একটি প্রাকটিক যুক্তি এবং একটি রূপান্তর ব্লক লাগে। @ হিরোলাউ এই প্রশ্নের একমাত্র সঠিক উত্তর দেয়।
inopinatus

4
রুবি ২.7 filter_mapএই সঠিক উদ্দেশ্যে প্রবর্তন করছে । আরও তথ্য এখানে
এসআরাক

উত্তর:


116

আমি সাধারণত ব্যবহার mapএবং compactএকসঙ্গে একটি পোস্টসাফিক্স হিসাবে আমার নির্বাচন মানদণ্ড সহ ifcompactনীল থেকে মুক্তি পায়।

jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}    
 => [3, 3, 3, nil, nil, nil] 


jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact
 => [3, 3, 3] 

4
আহ-হা, আমি কীভাবে আমার ম্যাপ ব্লকটি দিয়ে ফিরে আসা নীলগুলি উপেক্ষা করার চেষ্টা করার চেষ্টা করছিলাম। ধন্যবাদ!
শেঠ পেট্রি-জনসন

কোনও সমস্যা নেই, আমি কমপ্যাক্ট পছন্দ করি। এটি নিরবিচ্ছিন্নভাবে সেখানে বসে এবং তার কাজ করে। আমি এই পদ্ধতিটিকে সাধারণ নির্বাচনের মানদণ্ডের জন্য গণনাযোগ্য ফাংশনগুলি শৃঙ্খলার চেয়েও পছন্দ করি কারণ এটি অত্যন্ত ঘোষণামূলক।
জেড স্নাইডার

4
আমি নিশ্চিত না ছিল map+ + compactসত্যিই বেশী ভালো পড়তেন injectএবং সংশ্লিষ্ট থ্রেডে আমার বেঞ্চমার্ক ফলাফল পোস্ট stackoverflow.com/questions/310426/list-comprehension-in-ruby/...
knuton

4
এটি সমস্ত শূন্যস্থানগুলি মুছে ফেলবে, মূল শিল এবং আপনার মানদণ্ডে ব্যর্থ হওয়া উভয়ই। তাই দেখুন
ব্যবহারকারী 1143669

4
এটি পুরোপুরি শৃঙ্খলা অপসারণ করে না mapএবং selectএটি কেবল compactএকটি বিশেষ কেস rejectযা সরাসরি সি-তে বাস্তবায়িত হওয়ার কারণে শিলায় কাজ করে এবং কিছুটা ভাল সম্পাদন করে
জো এটজবার্গার

54

আপনি reduceএটির জন্য ব্যবহার করতে পারেন , যার জন্য কেবল একটি পাস প্রয়োজন:

[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a }
=> [3, 3, 3] 

অন্য কথায়, রাষ্ট্রটি আপনার যা চান তা হতে সূচনা করুন (আমাদের ক্ষেত্রে, শূন্য তালিকা পূরণ করতে []:), তবে সর্বদা মূল তালিকার প্রতিটি উপাদানটির পরিবর্তনের সাথে এই মানটি ফেরত নিশ্চিত করুন (আমাদের ক্ষেত্রে, পরিবর্তিত উপাদানটি তালিকার দিকে ঠেলা)।

এটি সর্বাধিক দক্ষ কারণ এটি কেবলমাত্র একটি পাসের সাথে তালিকার উপরে চলে আসে ( map+ selectঅথবা compactদুটি পাসের প্রয়োজন)।

আপনার ক্ষেত্রে:

def example
  results = @lines.reduce([]) do |lines, line|
    lines.push( ...(line) ) if ...
    lines
  end
  return results.uniq.sort
end

20
না each_with_objectএকটু বেশি জানার জন্য? ব্লকের প্রতিটি পুনরাবৃত্তির শেষে আপনাকে অ্যারেটি ফেরত দিতে হবে না। আপনি সহজভাবে করতে পারেন my_array.each_with_object([]) { |i, a| a << i if i.condition }
হেনরেবোথা

@henrebotha সম্ভবত এটি হয়। আমি একটি কার্যকরী ব্যাকগ্রাউন্ড থেকে আসছি, এজন্য আমি reduceপ্রথম খুঁজে পেয়েছি
অ্যাডাম লিন্ডবার্গ

39

রুবি ২.7+

সেখানে এখন!

রুবি ২.7 filter_mapএই সঠিক উদ্দেশ্যে প্রবর্তন করছে । এটি মূর্খতাবাদী এবং অভিনয়, এবং আমি আশা করি এটি খুব শীঘ্রই আদর্শ হয়ে উঠবে।

উদাহরণ স্বরূপ:

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

বিষয়টিতে একটি ভাল পড়া এখানে ।

আশা করি এটি কারও উপকারী!


4
আমি যতবারই আপগ্রেড করি না কেন, শীতল বৈশিষ্ট্যটি সর্বদা পরবর্তী সংস্করণে থাকে।
mlt

ভাল লাগল একটি সমস্যা হতে পারে যেহেতু filter, selectএবং find_allসমার্থক, ঠিক যেমনটি mapএবং collectএখনও, এই পদ্ধতির নামটি মনে রাখা কঠিন হতে পারে। এটা filter_map, select_collect, find_all_mapবা filter_collect?
এরিক ডুমিনিল

19

এর কাছে যাওয়ার আরও একটি ভিন্ন উপায় হ'ল নতুন (এই প্রশ্নের সাথে সম্পর্কিত) ব্যবহার করছে Enumerator::Lazy:

def example
  @lines.lazy
        .select { |line| line.property == requirement }
        .map    { |line| transforming_method(line) }
        .uniq
        .sort
end

.lazyপদ্ধতি একটি অলস গণনাকারী ফেরৎ। কল করা .selectবা .mapঅলস গণকের সাথে অন্য একটি অলস গুণককে ফেরত দেয়। কেবল একবার আপনি কল .uniqকরলে এটি প্রকৃতপক্ষে গণককে বাধ্য করে এবং একটি অ্যারে ফিরিয়ে দেয়। সুতরাং কার্যকরভাবে যা ঘটে তা হ'ল আপনার .selectএবং .mapকলগুলি একত্রিত হয়ে যায় - আপনি কেবল @linesএকবারে পুনরাবৃত্তি করেন .selectএবং উভয়ই করতে পারেন .map

আমার প্রবৃত্তিটি হ'ল আদমের reduceপদ্ধতিটি আরও দ্রুত হবে তবে আমি মনে করি এটি অনেক বেশি পাঠযোগ্য।


এর প্রাথমিক পরিণতি হ'ল প্রতিটি পরবর্তী পদ্ধতি কলের জন্য কোনও মধ্যবর্তী অ্যারে অবজেক্ট তৈরি করা হয় না। একটি সাধারণ @lines.select.mapপরিস্থিতিতে, selectএকটি অ্যারে প্রদান করে যা এরপরে সংশোধিত হয় map, আবার অ্যারে ফিরে আসে। তুলনা করে, অলস মূল্যায়ন কেবল একবার অ্যারে তৈরি করে। আপনার প্রাথমিক সংগ্রহের বস্তু বড় হলে এটি কার্যকর। এটি আপনাকে অসীম গণনাকারীর সাথে কাজ করার ক্ষমতা দেয় - যেমন random_number_generator.lazy.select(&:odd?).take(10)


4
প্রতিটি তাদের নিজস্ব। আমার ধরণের সমাধানের সাহায্যে আমি পদ্ধতির নামগুলি এক নজরে দেখতে পারি এবং তাত্ক্ষণিকভাবে জানতে পারি যে আমি ইনপুট ডেটার একটি উপসেটটি রূপান্তর করতে চলেছি, এটি অনন্য করে তুলছি এবং এটি বাছাই করব। reduce"সমস্ত কিছু করুন" রূপান্তরটি সর্বদা আমার কাছে মোটামুটি অগোছালো বোধ করে।
হেনরেবোথা

4
@ অেনরেবোথা: আপনি কী বোঝাতে চেয়েছিলেন তা যদি আমি ভুল বুঝে থাকি তবে আমাকে ক্ষমা করুন, তবে এটি একটি অত্যন্ত গুরুত্বপূর্ণ বিষয়: "আপনি উভয়ই একবার করার জন্য কেবল একবারে পুনরাবৃত্তি করেন " এবং এই কথাটি বলা ঠিক নয় " ব্যবহারের অর্থ এই নয় যে অলস গণকের শৃঙ্খলাবদ্ধ অপারেশন অপারেশনগুলি একটি একক পুনরাবৃত্তিতে "ধসে পড়বে"। এটি একটি সংগ্রহের উপর অলস মূল্যায়ন আর্ট চেইন ক্রিয়াকলাপগুলির একটি সাধারণ ভুল বোঝাবুঝি। ( প্রথম উদাহরণে এবং ব্লকগুলির শুরুতে আপনি একটি বিবৃতি যুক্ত করে এটি পরীক্ষা করতে পারেন You তারা দেখতে পাবেন যে তারা একই সংখ্যক লাইন প্রিন্ট করেছে)@lines.select.map.lazyputsselectmap
pj

4
@ হেনরেবোথা: এবং আপনি যদি .lazyএটি সরিয়ে থাকেন তবে একই সংখ্যক বার মুদ্রণ করে। এটি আমার বক্তব্য — আপনার mapব্লক এবং আপনার selectব্লকটি অলস এবং আগ্রহী সংস্করণগুলিতে একই সংখ্যক বার কার্যকর করা হয়। অলস সংস্করণটি "আপনার .selectএবং .mapকলগুলি একত্রিত করে না "
pj

4
@pje: বস্তুতপক্ষে lazy সম্মিলন তাদের একটি উপাদান এটি ব্যর্থ হয় কারণ selectশর্ত প্রেরণ না হয়ে যায় map। অন্য কথায়: প্রিপেন্ডিং lazyপ্রতিস্থাপন selectএবং mapএকক সাথে মোটামুটি সমান reduce([])এবং "বুদ্ধিমানভাবে" selectব্লককে reduceফলাফলের অন্তর্ভুক্তির পূর্ব শর্ত হিসাবে তৈরি করে ।
হেনরেবোথা

4
@ হেনরেবোথা: আমি মনে করি এটি সাধারণভাবে অলস মূল্যায়নের জন্য একটি বিভ্রান্তিমূলক উপমা, কারণ অলসতা এই অ্যালগরিদমের সময়ের জটিলতা পরিবর্তন করে না। এটি আমার বক্তব্য: প্রতিটি ক্ষেত্রে একটি অলস নির্বাচন-পরে-মানচিত্র সর্বদা তার উত্সাহিত সংস্করণ হিসাবে একই সংখ্যার গণনা সম্পাদন করবে। এটি কোনও গতি বাড়ায় না, এটি কেবল প্রতিটি পুনরাবৃত্তির সম্পাদনের ক্রম পরিবর্তন করে — বিপরীত ক্রমে পূর্ববর্তী ফাংশনগুলি থেকে প্রয়োজনীয় হিসাবে চেনের "টান" মানগুলির শেষ ক্রিয়াটি।
pje

13

অপারেটর ( ) selectব্যবহার করতে পারে এমন একটি যদি আপনার থাকে তবে এটি একটি ভাল বিকল্প:case===grep

p [1,2,'not_a_number',3].grep(Integer){|x| -x } #=> [-1, -2, -3]

p ['1','2','not_a_number','3'].grep(/\D/, &:upcase) #=> ["NOT_A_NUMBER"]

আমাদের আরও জটিল যুক্তি লাগলে আমরা ল্যাম্বডাস তৈরি করতে পারি:

my_favourite_numbers = [1,4,6]

is_a_favourite_number = -> x { my_favourite_numbers.include? x }

make_awesome = -> x { "***#{x}***" }

my_data = [1,2,3,4]

p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]

এটি কোনও বিকল্প নয় - এটি প্রশ্নের একমাত্র সঠিক উত্তর।
inopinatus

@ ইনোপিন্যাটাস: আর নেই । যদিও এটি এখনও একটি ভাল উত্তর। আমি মনে করি না অন্যথায় কোনও ব্লকের সাথে গ্রেপ দেখা।
এরিক ডুমিনিল

9

আমি নিশ্চিত যে একটি আছে। গণনীয় মডিউল , যা যোগ করা selectএবং map, এক দেখায় না।

আপনাকে select_and_transformপদ্ধতিটিতে দুটি ব্লকে পাস করতে হবে, যা কিছুটা অপ্রয়োজনীয় আইএমএইচও হবে।

স্পষ্টতই, আপনি কেবল তাদের একসাথে শৃঙ্খল করতে পারেন, যা আরও পাঠযোগ্য:

transformed_list = lines.select{|line| ...}.map{|line| ... }

3

সহজ উত্তর:

আপনার যদি এন রেকর্ড থাকে এবং আপনি চান selectএবং mapশর্তের উপর ভিত্তি করে

records.map { |record| record.attribute if condition }.compact

এখানে, রেকর্ড এবং শর্ত থেকে আপনি যা চান তা যাবতীয় যাচাই করতে পারেন বৈশিষ্ট্যটি is

কমপ্যাক্ট হ'ল অযৌক্তিক শূন্যতার ঝলক দেওয়া যা শর্ত থেকে যদি বেরিয়ে আসে


4
শর্ত না থাকলে আপনি একইটি ব্যবহার করতে পারেন। আমার বন্ধু হিসাবে জিজ্ঞাসা।
স্ক। ইরফান

2

না, তবে আপনি এটি এটি করতে পারেন:

lines.map { |line| do_some_action if check_some_property  }.reject(&:nil?)

বা আরও ভাল:

lines.inject([]) { |all, line| all << line if check_some_property; all }

14
reject(&:nil?)মূলত একই compact
জার্গ ডব্লু মিট্টাগ

হ্যাঁ, তাই ইনজেকশন পদ্ধতিটি আরও ভাল।
ড্যানিয়েল ও'হারা

2

আমি মনে করি যে এই পদ্ধতিটি আরও পঠনযোগ্য, কারণ অ্যাকশনগুলি সংযুক্ত রয়েছে তা পরিষ্কার থাকা অবস্থায় ফিল্টার শর্তগুলি এবং ম্যাপ করা মানটি বিভক্ত করে:

results = @lines.select { |line|
  line.should_include?
}.map do |line|
  line.value_to_map
end

এবং, আপনার নির্দিষ্ট ক্ষেত্রে, resultভেরিয়েবলটি সমস্ত একসাথে বাদ দিন :

def example
  @lines.select { |line|
    line.should_include?
  }.map { |line|
    line.value_to_map
  }.uniq.sort
end

1
def example
  @lines.select {|line| ... }.map {|line| ... }.uniq.sort
end

রুবি ১.৯ এবং ১.৮. In এ আপনি পুনরাবৃত্তিকারীদের কেবল কোনও ব্লক না পেরে শৃঙ্খলাবদ্ধ এবং মোড়ানোও করতে পারেন:

enum.select.map {|bla| ... }

কিন্তু এটা এই ক্ষেত্রে সত্যিই সম্ভব নয়, ব্লক আগমন মূল্যবোধের ধরনের যেহেতু selectএবং mapআপ মিলছে না। এটি এর মতো কোনও কিছুর জন্য এটি আরও বোধ করে:

enum.inject.with_index {|(acc, el), idx| ... }

আফিক্স, আপনি সবচেয়ে ভাল করতে পারেন এর প্রথম উদাহরণ।

এখানে একটি ছোট উদাহরণ:

%w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["a", "b", "c", "d"]

%w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["A", "B", false, false, "C", "D"]

তা না হলে কি সত্যিই চান ["A", "B", "C", "D"]


"রুবিতে পদ্ধতিতে শৃঙ্খলাবদ্ধ" করার জন্য আমি গত রাতে খুব সংক্ষিপ্ত ওয়েব অনুসন্ধান করেছি এবং দেখে মনে হয়েছিল এটি ভালভাবে সমর্থন করে না। তবে, আমার সম্ভবত এটি চেষ্টা করা উচিত ছিল ... এছাড়াও, আপনি কেন বলছেন যে ব্লক যুক্তিগুলির ধরণগুলি মেলে না? আমার উদাহরণে উভয় ব্লক আমার অ্যারে থেকে পাঠ্যটির একটি লাইন নিচ্ছে, তাই না?
শেঠ পেট্রি-জনসন

@ শেঠ পেট্রি-জনসন: হ্যাঁ, দুঃখিত, আমি প্রত্যাবর্তনের মানগুলি বোঝাতে চাইছি। selectএকটি বুলিয়ান-ইশ মান প্রদান করে যা উপাদানটি রাখবে কি না তা স্থির করে, mapপরিবর্তিত মানটি দেয় returns রূপান্তরিত মান নিজেই সম্ভবত সত্যবাদী হতে চলেছে, সুতরাং সমস্ত উপাদান নির্বাচিত হয়।
জার্গ ডব্লু মিট্টাগ

1

আপনার আমার লাইব্রেরি রিয়ার্ড রুবি ব্যবহার করার চেষ্টা করা উচিত যাতে আমি পদ্ধতিটি যুক্ত করেছি Enumerable#select_map। এখানে একটি উদাহরণ:

items = [{version: "1.1"}, {version: nil}, {version: false}]

items.select_map{|x| x[:version]} #=> [{version: "1.1"}]
# or without enumerable monkey patch
Rearmed.select_map(items){|x| x[:version]}

select_mapএই লাইব্রেরিতে কেবল select { |i| ... }.map { |i| ... }উপরের অনেক উত্তর থেকে একই কৌশল প্রয়োগ করে ।
জর্ডান স্যাটকিন 9:58 এ 5 ই

1

আপনি যদি দুটি ভিন্ন অ্যারে তৈরি না করতে চান compact!তবে আপনি এটি ব্যবহার করতে পারেন তবে সে সম্পর্কে সতর্ক হন।

array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}
new_array.compact!

মজার বিষয় হল, compact!শূন্যস্থান অপসারণ স্থানে কাজ করে। compact!যদি পরিবর্তনগুলি হয় তবে শূন্যস্থান না থাকলে শূন্যের মানটি একই অ্যারে হয়।

array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }

ওয়ান লাইনার হবে


0

আপনার সংস্করণ:

def example
  matchingLines = @lines.select{ |line| ... }
  results = matchingLines.map{ |line| ... }
  return results.uniq.sort
end

আমার সংস্করণ:

def example
  results = {}
  @lines.each{ |line| results[line] = true if ... }
  return results.keys.sort
end

এটি 1 পুনরাবৃত্তি করবে (বাছাই করা বাদে), এবং স্বতন্ত্রতা রাখার যুক্ত বোনাস রয়েছে (যদি আপনি ইউনিট সম্পর্কে চিন্তা করেন না, তবে কেবল ফলাফলগুলিকে অ্যারে করুন এবং results.push(line) if ...


-1

এখানে একটি উদাহরণ। এটি আপনার সমস্যার মতো নয়, তবে আপনি যা চান তা হতে পারে বা আপনার সমাধানের জন্য একটি সূত্র দিতে পারেন:

def example
  lines.each do |x|
    new_value = do_transform(x)
    if new_value == some_thing
      return new_value    # here jump out example method directly.
    else
      next                # continue next iterate.
    end
  end
end
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.