How do you iterate through an amalgamation of two Enumerables efficiently? - ruby

Given
a = nil # or [1,2]
b = [1,2] # or nil
Can you iterate through the concatenation of a and b without allocating an intermediate or creating massive amount of boiler plate code?
# meaning do this much more efficiently
((a || []) + (b || [])).each do |thing|
# more lines here
puts thing
end
This is kind of ugly:
l = lambda{|thing| do_my_thing }
a.each{|thing| l.call(thing)} if a
b.each{|thing| l.call(thing)} if b

Well, if you're willing to create a container (which should be cheap even if the elements contained are large), you could:
[a,b].compact.each do |e|
e.each do
# stuff
end
end
You do have to create a container array, but since you don't have to copy the contents of the sub-arrays (and instead are just dealing with two array pointers), it should be very quick and not terrible on the GC.
An alternate solution might just be to create a method:
def multi_each(*args,&block)
args.each do |a|
a.each(&block) if a
end
end
multi_each(nil,[1],[2,3]) do |i|
puts i
end
# 1
# 2
# 3

If you are using 1.9, I would use the ability for multiple splats:
a = nil
b = [1, 2]
[*a, *b]
#=> [1, 2]
a = [3, 4]
b = nil
[*a, *b]
#=> [3, 4]
So [*a, *b].each {} seems exactly like what you want.

What you have can be made far more concise and considerably less ugly by passing the lambda as the block itself:
l = lambda { |thing| do_my_thing }
a.each(&l) if a
b.each(&l) if b

Related

Ruby inject with index and brackets

I try to clean my Code. The first Version uses each_with_index. In the second version I tried to compact the code with the Enumerable.inject_with_index-construct, that I found here.
It works now, but seems to me as obscure as the first code.
Add even worse I don't understand the brackets around element,index in
.. .inject(groups) do |group_container, (element,index)|
but they are necessary
What is the use of these brackets?
How can I make the code clear and readable?
FIRST VERSION -- WITH "each_with_index"
class Array
# splits as good as possible to groups of same size
# elements are sorted. I.e. low elements go to the first group,
# and high elements to the last group
#
# the default for number_of_groups is 4
# because the intended use case is
# splitting statistic data in 4 quartiles
#
# a = [1, 8, 7, 5, 4, 2, 3, 8]
# a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]
#
# b = [[7, 8, 9], [4, 5, 7], [2, 8]]
# b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index do |element, index|
group_number = (index.to_f / average_group_size).floor
groups[group_number] << element
end
groups
end
end
SECOND VERSION -- WITH "inject" AND index
class Array
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index.inject(groups) do |group_container, (element,index)|
group_number = (index.to_f / average_group_size).floor
group_container[group_number] << element
group_container
end
end
end
What is the use of these brackets?
It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.
Here's how it works. Let's say you have an array
arr = [1, 2, 3]
Then you assign this array to a list of names, like this:
a, b, c = arr
a # => 1
b # => 2
c # => 3
You see, the array was "destructured" into its individual elements. Now, to the each_with_index. As you know, it's like a regular each, but also returns an index. inject doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from each_with_index), then we can either take it apart in the block body
sorted.each_with_index.inject(groups) do |group_container, pair|
element, index = pair
# or
# element = pair[0]
# index = pair[1]
# rest of your code
end
Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.
Hope this helps.
lines = %w(a b c)
indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
acc << ind - 1 if el == "b"
acc
end
indexes # => [0]
What is the use of these brackets?
To understand the brackets, first you need to understand how destruction works in ruby. The simplest example I can think of this this:
1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
1.8.7 :002 > puts a, b
1.8.7 :003?> end
1
3
2
4
You should know how each function works, and that the block receives one parameter. So what happens when you pass two parameters? It takes the first element [1,3] and try to split (destruct) it in two, and the result is a=1 and b=3.
Now, inject takes two arguments in the block parameter, so it is usually looks like |a,b|. So passing a parameter like |group_container, (element,index)| we are in fact taking the first one as any other, and destructing the second in two others (so, if the second parameter is [1,3], element=1 and index=3). The parenthesis are needed because if we used |group_container, element, index| we would never know if we are destructing the first or the second parameter, so the parenthesis there works as disambiguation.
9In fact, things works a bit different in the bottom end, but lets hide this for this given question.)
Seems like there already some answers given with good explanation. I want to add some information regards the clear and readable.
Instead of the solution you chose, it is also a possibility to extend Enumerable and add this functionality.
module Enumerable
# The block parameter is not needed but creates more readable code.
def inject_with_index(memo = self.first, &block)
skip = memo.equal?(self.first)
index = 0
self.each_entry do |entry|
if skip
skip = false
else
memo = yield(memo, index, entry)
end
index += 1
end
memo
end
end
This way you can call inject_with_index like so:
# m = memo, i = index, e = entry
(1..3).inject_with_index(0) do |m, i, e|
puts "m: #{m}, i: #{i}, e: #{e}"
m + i + e
end
#=> 9
If you not pass an initial value the first element will be used, thus not executing the block for the first element.
In case, someone is here from 2013+ year, you have each_with_object and with_index for your needs:
records.each_with_object({}).with_index do |(record, memo), index|
memo[record.uid] = "#{index} in collection}"
end

Did the `each` method change its behavior?

A Ruby exercise about multidimensional array said that two instances of each method are necessary to access the inner elements of a multidimensional array. The following:
x = [[1,2],[3,4],[5,6]]
x.each do |a|
a.each do |b|
puts b
end
end
should return:
# 1
# 2
# 3
# 4
# 5
# 6
However, it's not necessary to use two each methods. If I just do
x.each { |a| puts a }
I get the same result. It seems a single instance of each already goes to the inner level of multidimensional arrays.
In that case, how would I access the first level? In other words, how would I get the following?
# [1,2]
# [3,4]
# [5,6]
There are three different print functions in Ruby. Let's try them in the Ruby prompt:
> puts [1,2]
1
2
=> nil
> p [1,2]
[1, 2]
=> [1, 2]
> print [1,2]
[1, 2]=> nil
In case you aren't familiar with irb, the expression following the fat arrow => is the return value of the statement.
Moreover, if you do just
puts x
you'll get exactly the same result. This is because puts treats arrays in a special manner. It enumerates all elements and calls puts on them individually. (this is recursive, as you might imagine).
This will get roughly the output you want:
x.each {|a| p a}
or
x.each {|a| puts a.inspect }
Output
# >> [1, 2]
# >> [3, 4]
# >> [5, 6]
x.each { |a| puts a }
This will call puts on each element of the x array.
It is the same as doing :
puts [1,2]
puts [3,4]
puts [5,6]
puts on an array will format it like you saw.

Check to see if an array is already sorted?

I know how to put an array in order, but in this case I just want to see if it is in order. An array of strings would be the easiest, I imagine, and answers on that front are appreciated, but an answer that includes the ability to check for order based on some arbitrary parameter is optimal.
Here's an example dataset. The name of:
[["a", 3],["b",53],["c",2]]
Where the elements are themselves arrays containing several elements, the first of which is a string. I want to see if the elements are in alphabetical order based on this string.
It looks like a generic abstraction, let's open Enumerable:
module Enumerable
def sorted?
each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end
end
[["a", 3], ["b", 53],["c", 2]].sorted? #=> true
Notice that we have to write (a <=> b) <= 0 instead of a <= b because there are classes that support <=> but not the comparator operators (i.e. Array), since they do not include the module Comparable.
You also said you'd like to have the ability "to check for order based on some arbitrary parameter":
module Enumerable
def sorted_by?
each_cons(2).all? { |a, b| ((yield a) <=> (yield b)) <= 0 }
end
end
[["a", 3], ["b", 1], ["c", 2]].sorted_by? { |k, v| v } #=> false
Using lazy enumerables (Ruby >= 2.1), we can reuse Enumerable#sorted?:
module Enumerable
def sorted_by?(&block)
lazy.map(&block).sorted?
end
end
You can compare them two by two:
[["a", 3],["b",53],["c",2]].each_cons(2).all?{|p, n| (p <=> n) != 1} # => true
reduce can compare each element to the one before, and stop when it finds one out of order:
array.reduce{|prev,l| break unless l[0] >= prev[0]; l}
If it turns out the array isn't sorted, will your next action always be to sort it? For that use case (though of course depending on the number of times the array will already be sorted), you may not want to check whether it is sorted, but instead simply choose to always sort the array. Sorting an already sorted array is pretty efficient with many algorithms and merely checking whether an array is already sorted is not much less work, making checking + sorting more work than simply always sorting.
def ascending? (array)
yes = true
array.reduce { |l, r| break unless yes &= (l[0] <= r[0]); l }
yes
end
def descending? (array)
yes = true
array.reduce { |l, r| break unless yes &= (l[0] >= r[0]); l }
yes
end
Iterate over the objects and make sure each following element is >= the current element (or previous is <=, obviously) the current element.
For this to work efficiently you will want to sort during insertion.
If you are dealing with unique items, a SortedSet is also an option.
For clarification, if we patch array to allow for a sorted insertion, then we can keep the array in a sorted state:
class Array
def add_sorted(o)
size = self.size
if size == 0
self << o
elsif self.last < o
self << o
elsif self.first > o
self.insert(0, o)
else
# This portion can be improved by using a binary search instead of linear
self.each_with_index {|n, i| if n > o; self.insert(i, o); break; end}
end
end
end
a = []
12.times{a.add_sorted(Random.rand(10))}
p a # => [1, 1, 2, 2, 3, 4, 5, 5, 5, 5, 7]
or to use the built in sort:
class Array
def add_sorted2(o)
self << o
self.sort
end
end
or, if you are dealing with unique items:
require "set"
b = SortedSet.new
12.times{b << Random.rand(10)}
p b # => #<SortedSet: {1, 3, 4, 5, 6, 7, 8, 9}>
These are all way too hard. You don't have to sort, but you can use sort to check. Scrambled array below for demonstration purposes.
arr = [["b",3],["a",53],["c",2]]
arr.sort == arr # => false
p arr.sort # => [["a",53],["b",3],["c",2]]

Overriding elements of an array in "each" loop

a = [1, 2, 3]
a.each do |x| x+=10 end
After this operation array a is still [1, 2, 3]. How to convert it into [11, 12, 13]?
Use the collect! method:
a = [1, 2, 3]
a.collect!{ |x| x + 10 }
There are two general classes of solutions:
Imperative object-mutating code
a.map! { |x| x + 10 }
An almost functional solution
a = a.map { |x| x + 10 }
Both techniques have their place.
I like the aliased name "map" myself. It has less characters.
The difference with these methods as compared to what you've done is two fold. One is that you have to use a method that modifies the initial array (typically these are the bang methods, or the methods which have a name ending in a ! (map!, collect!, ...) The second thing is that a.each is the method typically used for just going through the array to use the individual elements. Map or Collect methods return an array containing a return from each iteration of the block.
Hence, you could have done the following:
a = [1,2,3]
b = []
a.each do |x|
b << x+10
end
or you could use the map or collect method as demonstrated by dmarko or as here:
a = [1,2,3]
a = a.map {|x| x+10}

Skip over iteration in Enumerable#collect

(1..4).collect do |x|
next if x == 3
x + 1
end # => [2, 3, nil, 5]
# desired => [2, 3, 5]
If the condition for next is met, collect puts nil in the array, whereas what I'm trying to do is put no element in the returned array if the condition is met. Is this possible without calling delete_if { |x| x == nil } on the returned array?
My code excerpt is heavily abstracted, so looking for a general solution to the problem.
There is method Enumerable#reject which serves just the purpose:
(1..4).reject{|x| x == 3}.collect{|x| x + 1}
The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.
BTW, map (or collect) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable.
Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject (or its similar method named each_with_object):
(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
I would simply call .compact on the resultant array, which removes any instances of nil in an array. If you'd like it to modify the existing array (no reason not to), use .compact!:
(1..4).collect do |x|
next if x == 3
x
end.compact!
In Ruby 2.7+, it’s possible to use filter_map for this exact purpose. From the docs:
Returns an array containing truthy elements returned by the block.
(0..9).filter_map {|i| i * 2 if i.even? } #=> [0, 4, 8, 12, 16]
{foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } #=> [:foo, :baz]
For the example in the question: (1..4).filter_map { |x| x + 1 unless x == 3 }.
See this post for comparison with alternative methods, including benchmarks.
just a suggestion, why don't you do it this way:
result = []
(1..4).each do |x|
next if x == 3
result << x
end
result # => [1, 2, 4]
in that way you saved another iteration to remove nil elements from the array. hope it helps =)
i would suggest to use:
(1..4).to_a.delete_if {|x| x == 3}
instead of the collect + next statement.
You could pull the decision-making into a helper method, and use it via Enumerable#reduce:
def potentially_keep(list, i)
if i === 3
list
else
list.push i
end
end
# => :potentially_keep
(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]

Resources