Related
So I need to create an instance method for Array that takes two arguments, the size of an array and an optional object that will be appended to an array.
If the the size argument is less than or equal to the Array.length or the size argument is equal to 0, then just return the array. If the optional argument is left blank, then it inputs nil.
Example output:
array = [1,2,3]
array.class_meth(0) => [1,2,3]
array.class_meth(2) => [1,2,3]
array.class_meth(5) => [1,2,3,nil,nil]
array.class_meth(5, "string") => [1,2,3,"string","string"]
Here is my code that I've been working on:
class Array
def class_meth(a ,b=nil)
self_copy = self
diff = a - self_copy.length
if diff <= 0
self_copy
elsif diff > 0
a.times {self_copy.push b}
end
self_copy
end
def class_meth!(a ,b=nil)
# self_copy = self
diff = a - self.length
if diff <= 0
self
elsif diff > 0
a.times {self.push b}
end
self
end
end
I've been able to create the destructive method, class_meth!, but can't seem to figure out a way to make it non-destructive.
Here's (IMHO) a cleaner solution:
class Array
def class_meth(a, b = nil)
clone.fill(b, size, a - size)
end
def class_meth!(a, b = nil)
fill(b, size, a - size)
end
end
I think it should meet all your needs. To avoid code duplication, you can make either method call the other one (but not both simulaneously, of course):
def class_meth(a, b = nil)
clone.class_meth!(a, b)
end
or:
def class_meth!(a, b = nil)
replace(class_meth(a, b))
end
As you problem has been diagnosed, I will just offer a suggestion for how you might do it. I assume you want to pass two and optionally three, not one and optionally two, parameters to the method.
Code
class Array
def self.class_meth(n, arr, str=nil)
arr + (str ? ([str] : [nil]) * [n-arr.size,0].max)
end
end
Examples
Array.class_meth(0, [1,2,3])
#=> [1,2,3]
Array.class_meth(2, [1,2,3])
#=> [1,2,3]
Array.class_meth(5, [1,2,3])
#=> [1,2,3,nil,nil]
Array.class_meth(5, [1,2,3], "string")
#=> [1,2,3,"string","string"]
Array.class_meth(5, ["dog","cat","pig"])
#=> [1,2,3,"string","string"]
Array.class_meth(5, ["dog","cat","pig"], "string")
#=> [1,2,3,"string","string"]
Array.class_meth(5, ["dog","cat","pig"])
#=> ["dog", "cat", "pig", nil, nil]
Array.class_meth(5, ["dog","cat","pig"], "string")
#=> ["dog", "cat", "pig", "string", "string"]
Before withdrawing his answer, #PatriceGahide suggested using Array#fill. That would be an improvement here; i.e., replace the operative line with:
arr.fill(str ? str : nil, arr.size, [n-arr.size,0].max)
self_copy = self does not make a new object - assignment in Ruby never "copies" or creates a new object implicitly.
Thus the non-destructive case works on the same object (the instance the method was invoked upon) as in the destructive case, with a different variable bound to the same object - that is self.equal? self_copy is true.
The simplest solution is to merely use #clone, keeping in mind it is a shallow clone operation:
def class_meth(a ,b=nil)
self_copy = self.clone # NOW we have a new object ..
# .. so we can modify the duplicate object (self_copy)
# down here without affecting the original (self) object.
end
If #clone cannot be used other solutions involve create a new array or obtain an array #slice (returns a new array) or even append (returning a new array) with #+; however, unlike #clone, these generally lock-into returning an Array and not any sub-type as may be derived.
After the above change is made it should also be apparent that it can written as so:
def class_meth(a ,b=nil)
clone.class_meth!(a, b) # create a NEW object; modify it; return it
# (assumes class_meth! returns the object)
end
A more appropriate implementation of #class_meth!, or #class_meth using one of the other forms to avoid modification of the current instance, is left as an exercise.
FWIW: Those are instance methods, which is appropriate, and not "class meth[ods]"; don't be confused by the ill-naming.
In Ruby it is bad style to use for-loops. This is commonly understood.
A style guide recommended to me:
(https://github.com/bbatsov/ruby-style-guide#source-code-layout)
says:
"Never use for, unless you know exactly why. Most of the time iterators should be used instead. for is implemented in terms of each (so you're adding a level of indirection), but with a twist - for doesn't introduce a new scope (unlike each) and variables defined in its block will be visible outside it."
The example given is:
arr = [1, 2, 3]
#bad
for elem in arr do
puts elem
end
# good
arr.each { |elem| puts elem }
I have researched and I can't find an explanation as to how to simulate a for loop that provides an iterating value I can pass to places or perform arithmetic on.
For example, with what would I replace:
for i in 0...size do
puts array1[i]
puts array2[size-1 - i]
puts i % 2
end
It's easy if it's one array, but I often need the current position for other purposes.
There's either a simple solution I'm missing, or situations where for is required. Additionally, I hear people talk about for as if it is never needed. What then is their solution to this?
Can it be improved? And what is the solution, if there is one? Thanks.
If you want to iterate over a collection and keep track of the index, use each_with_index:
fields = ["name", "age", "height"]
fields.each_with_index do |field,i|
puts "#{i}. #{field}" # 0. name, 1. age, 2. height
end
Your for i in 0...size example becomes:
array1.each_with_index do |item, i|
puts item
puts array2[size-1 - i]
puts i % 2
end
Don't forget you can do cool things like this too
fields = ["name", "age", "height"]
def output name, idx
puts "#{idx}. #{name}"
end
fields.each_with_index &method(:output)
Output
0. name
1. age
2. height
You can use this technique as a class or instance method too
class Printer
def self.output data
puts "raw: #{data}"
end
end
class Kanon < Printer
def initialize prefix
#prefix = prefix
end
def output data
puts "#{#prefix}: #{data}"
end
end
def print printer, data
# separating the block from `each` allows
# you to do interesting things
data.each &printer.method(:output)
end
example using class method
print Printer, ["a", "b", "c"]
# raw: a
# raw: b
# raw: c
example using instance method
kanon = Kanon.new "kanon prints pretty"
print kanon, ["a", "b", "c"]
# kanon prints pretty: a
# kanon prints pretty: b
# kanon prints pretty: c
I have a Ruby array containing some string values. I need to:
Find all elements that match some predicate
Run the matching elements through a transformation
Return the results as an array
Right now my solution looks like this:
def example
matchingLines = #lines.select{ |line| ... }
results = matchingLines.map{ |line| ... }
return results.uniq.sort
end
Is there an Array or Enumerable method that combines select and map into a single logical statement?
I usually use map and compact together along with my selection criteria as a postfix if. compact gets rid of the nils.
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]
Ruby 2.7+
There is now!
Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.
For example:
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
Here's a good read on the subject.
Hope that's useful to someone!
You can use reduce for this, which requires only one pass:
[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a }
=> [3, 3, 3]
In other words, initialize the state to be what you want (in our case, an empty list to fill: []), then always make sure to return this value with modifications for each element in the original list (in our case, the modified element pushed to the list).
This is the most efficient since it only loops over the list with one pass (map + select or compact requires two passes).
In your case:
def example
results = #lines.reduce([]) do |lines, line|
lines.push( ...(line) ) if ...
lines
end
return results.uniq.sort
end
Another different way of approaching this is using the new (relative to this question) Enumerator::Lazy:
def example
#lines.lazy
.select { |line| line.property == requirement }
.map { |line| transforming_method(line) }
.uniq
.sort
end
The .lazy method returns a lazy enumerator. Calling .select or .map on a lazy enumerator returns another lazy enumerator. Only once you call .uniq does it actually force the enumerator and return an array. So what effectively happens is your .select and .map calls are combined into one - you only iterate over #lines once to do both .select and .map.
My instinct is that Adam's reduce method will be a little faster, but I think this is far more readable.
The primary consequence of this is that no intermediate array objects are created for each subsequent method call. In a normal #lines.select.map situation, select returns an array which is then modified by map, again returning an array. By comparison, the lazy evaluation only creates an array once. This is useful when your initial collection object is large. It also empowers you to work with infinite enumerators - e.g. random_number_generator.lazy.select(&:odd?).take(10).
If you have a select that can use the case operator (===), grep is a good alternative:
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"]
If we need more complex logic we can create lambdas:
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***"]
I'm not sure there is one. The Enumerable module, which adds select and map, doesn't show one.
You'd be required to pass in two blocks to the select_and_transform method, which would be a bit unintuitive IMHO.
Obviously, you could just chain them together, which is more readable:
transformed_list = lines.select{|line| ...}.map{|line| ... }
Simple Answer:
If you have n records, and you want to select and map based on condition then
records.map { |record| record.attribute if condition }.compact
Here, attribute is whatever you want from the record and condition you can put any check.
compact is to flush the unnecessary nil's which came out of that if condition
No, but you can do it like this:
lines.map { |line| do_some_action if check_some_property }.reject(&:nil?)
Or even better:
lines.inject([]) { |all, line| all << line if check_some_property; all }
I think that this way is more readable, because splits the filter conditions and mapped value while remaining clear that the actions are connected:
results = #lines.select { |line|
line.should_include?
}.map do |line|
line.value_to_map
end
And, in your specific case, eliminate the result variable all together:
def example
#lines.select { |line|
line.should_include?
}.map { |line|
line.value_to_map
}.uniq.sort
end
def example
#lines.select {|line| ... }.map {|line| ... }.uniq.sort
end
In Ruby 1.9 and 1.8.7, you can also chain and wrap iterators by simply not passing a block to them:
enum.select.map {|bla| ... }
But it's not really possible in this case, since the types of the block return values of select and map don't match up. It makes more sense for something like this:
enum.inject.with_index {|(acc, el), idx| ... }
AFAICS, the best you can do is the first example.
Here's a small example:
%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"]
But what you really want is ["A", "B", "C", "D"].
You should try using my library Rearmed Ruby in which I have added the method Enumerable#select_map. Heres an example:
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]}
If you want to not create two different arrays, you can use compact! but be careful about it.
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}
new_array.compact!
Interestingly, compact! does an in place removal of nil. The return value of compact! is the same array if there were changes but nil if there were no nils.
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }
Would be a one liner.
Your version:
def example
matchingLines = #lines.select{ |line| ... }
results = matchingLines.map{ |line| ... }
return results.uniq.sort
end
My version:
def example
results = {}
#lines.each{ |line| results[line] = true if ... }
return results.keys.sort
end
This will do 1 iteration (except the sort), and has the added bonus of keeping uniqueness (if you don't care about uniq, then just make results an array and results.push(line) if ...
Here is a example. It is not the same as your problem, but may be what you want, or can give a clue to your solution:
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
Why would you create a proxy reference to an object in Ruby, by using the to_enum method rather than just using the object directly? I cannot think of any practical use for this, trying to understand this concept & where someone might use it, but all the examples I have seen seem very trivial.
For example, why use:
"hello".enum_for(:each_char).map {|c| c.succ }
instead of
"hello".each_char.map {|c| c.succ }
I know this is a very simple example, does anyone have any real-world examples?
Most built-in methods that accept a block will return an enumerator in case no block is provided (like String#each_char in your example). For these, there is no reason to use to_enum; both will have the same effect.
A few methods do not return an Enumerator, though. In those case you might need to use to_enum.
# How many elements are equal to their position in the array?
[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index} #=> 2
As another example, Array#product, #uniq and #uniq! didn't use to accept a block. In 1.9.2, this was changed, but to maintain compatibility, the forms without a block can't return an Enumerator. One can still "manually" use to_enum to get an enumerator:
require 'backports/1.9.2/array/product' # or use Ruby 1.9.2+
# to avoid generating a huge intermediary array:
e = many_moves.to_enum(:product, many_responses)
e.any? do |move, response|
# some criteria
end
The main use of to_enum is when you are implementing your own iterative method. You typically will have as a first line:
def my_each
return to_enum :my_each unless block_given?
# ...
end
I think it has something to do with internal and external Iterators. When you return an enumerator like this:
p = "hello".enum_for(:each_char)
p is an external enumerator. One advantage of external iterators is that:
External iterators are more flexible than internal iterators. It's easy to compare two collections for equality with an external iterator, for example, but it's practically impossible with internal iterators…. But on the other hand, internal iterators are easier to use, because they define the iteration logic for you. [From The Ruby Programming Language book, ch. 5.3]
So, with external iterator you can do, e.g.:
p = "hello".enum_for(:each_char)
loop do
puts p.next
end
Let's say we want to take an array of keys and an array of values and sew them up in a Hash:
With #to_enum
def hashify(k, v)
keys = k.to_enum(:each)
values = v.to_enum(:each)
hash = []
loop do
hash[keys.next] = values.next
# No need to check for bounds,
# as #next will raise a StopIteration which breaks from the loop
end
hash
end
Without #to_enum:
def hashify(k, v)
hash = []
keys.each_with_index do |key, index|
break if index == values.length
hash[key] = values[index]
end
hash
end
It's much easier to read the first method, don't you think? Not a ton easier, but imagine if we were somehow manipulating items from 3 arrays? 5? 10?
This isn't quite an answer to your question, but hopefully it is relevant.
In your second example you are calling each_char without passing a block. When called without a block each_char returns an Enumerator so your examples are actually just two ways of doing the same thing. (i.e. both result in the creation of an enumerable object.)
irb(main):016:0> e1 = "hello".enum_for(:each_char)
=> #<Enumerator:0xe15ab8>
irb(main):017:0> e2 = "hello".each_char
=> #<Enumerator:0xe0bd38>
irb(main):018:0> e1.map { |c| c.succ }
=> ["i", "f", "m", "m", "p"]
irb(main):019:0> e2.map { |c| c.succ }
=> ["i", "f", "m", "m", "p"]
It's great for large or infinite generator objects.
E.g., the following will give you an enumerator for the whole Fibonacci seequence, from 0 to infinity.
def fib_sequence
return to_enum(:fib_sequence) unless block_given?
yield 0
yield 1
x,y, = 0, 1
loop { x,y = y,x+y; yield(y) }
end
to_enum effectively allows you to write this with regular yields without having to mess with Fibers.
You can then slice it as you want, and it will be very memory efficient, since no arrays will be stored in memory:
module Slice
def slice(range)
return to_enum(:slice, range) unless block_given?
start, finish = range.first, range.max + 1
copy = self.dup
start.times { copy.next }
(finish-start).times { yield copy.next }
end
end
class Enumerator
include Slice
end
fib_sequence.slice(0..10).to_a
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
fib_sequence.slice(10..20).to_a
#=> [55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
In Ruby, what is the most expressive way to map an array in such a way that certain elements are modified and the others left untouched?
This is a straight-forward way to do it:
old_a = ["a", "b", "c"] # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) } # ["a", "b!", "c"]
Omitting the "leave-alone" case of course if not enough:
new_a = old_a.map { |x| x+"!" if x=="b" } # [nil, "b!", nil]
What I would like is something like this:
new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"})
do |y|
y + "!"
end
# ["a", "b!", "c"]
Is there some nice way to do this in Ruby (or maybe Rails has some kind of convenience method that I haven't found yet)?
Thanks everybody for replying. While you collectively convinced me that it's best to just use map with the ternary operator, some of you posted very interesting answers!
Because arrays are pointers, this also works:
a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }
puts a.inspect
# => ["hello", "to!", "you!", "dude"]
In the loop, make sure you use a method that alters the object rather than creating a new object. E.g. upcase! compared to upcase.
The exact procedure depends on what exactly you are trying to achieve. It's hard to nail a definite answer with foo-bar examples.
old_a.map! { |a| a == "b" ? a + "!" : a }
gives
=> ["a", "b!", "c"]
map! modifies the receiver in place, so old_a is now that returned array.
I agree that the map statement is good as it is. It's clear and simple,, and would easy
for anyone to maintain.
If you want something more complex, how about this?
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"
Your map solution is the best one. I'm not sure why you think map_modifying_only_elements_where is somehow better. Using map is cleaner, more concise, and doesn't require multiple blocks.
One liner:
["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }
In the code above, you start with [] "cumulative". As you enumerate through an Enumerator (in our case the array, ["a", "b", "c"]), cumulative as well as "the current" item get passed to our block (|cumulative, i|) and the result of our block's execution is assigned to cumulative. What I do above is keep cumulative unchanged when the item isn't "b" and append "b!" to cumulative array and return it when it is a b.
There is an answer above that uses select, which is the easiest way to do (and remember) it.
You can combine select with map in order to achieve what you're looking for:
arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
=> ["b!"]
Inside the select block, you specify the conditions for an element to be "selected". This will return an array. You can call "map" on the resulting array to append the exclamation mark to it.
Ruby 2.7+
As of 2.7 there's a definitive answer.
Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.
For example:
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
Here's a good read on the subject.
Hope that's useful to someone!
If you don't need the old array, I prefer map! in this case because you can use the ! method to represent you are changing the array in place.
self.answers.map!{ |x| (x=="b" ? x+"!" : x) }
I prefer this over:
new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }
It's a few lines long, but here's an alternative for the hell of it:
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"]
The collect with ternary seems best in my opinion.
I've found that the best way to accomplish this is by using tap
arr = [1,2,3,4,5,6]
[].tap do |a|
arr.each { |x| a << x if x%2==0 }
end