Looping through an array with step - ruby

I want to look at every n-th elements in an array. In C++, I'd do this:
for(int x = 0; x<cx; x+=n){
value_i_care_about = array[x];
//do something with the value I care about.
}
I want to do the same in Ruby, but can't find a way to "step". A while loop could do the job, but I find it distasteful using it for a known size, and expect there to be a better (more Ruby) way of doing this.

Ranges have a step method which you can use to skip through the indexes:
(0..array.length - 1).step(2).each do |index|
value_you_care_about = array[index]
end
Or if you are comfortable using ... with ranges the following is a bit more concise:
(0...array.length).step(2).each do |index|
value_you_care_about = array[index]
end

array.each_slice(n) do |e, *_|
value_i_care_about = e
end

Just use step() method from Range class which returns an enumerator
(1..10).step(2) {|x| puts x}

We can iterate while skipping over a range of numbers on every iteration e.g.:
1.step(10, 2) { |i| print "#{i} "}
http://www.skorks.com/2009/09/a-wealth-of-ruby-loops-and-iterators/
So something like:
array.step(n) do |element|
# process element
end

class Array
def step(interval, &block)
((interval -1)...self.length).step(interval) do |value|
block.call(self[value])
end
end
end
You could add the method to the class Array

What about:
> [1, 2, 3, 4, 5, 6, 7].select.each_with_index { |_,i| i % 2 == 0 }
=> [1, 3, 5, 7]
Chaining of iterators is very useful.

This is a great example for the use of the modulo operator %
When you grasp this concept, you can apply it in a great number of different programming languages, without having to know them in and out.
step = 2
["1st","2nd","3rd","4th","5th","6th"].each_with_index do |element, index|
puts element if index % step == 1
end
#=> "2nd"
#=> "4th"
#=> "6th"

Related

Move element by index in Ruby

arr = [50, 30, "+"]
I would like to move "+" in between 50 and 30 through iteration.
I thought each_with_index would do the trick but so far no luck. This is a part of the process of making Reverse Polish Notation. (If anyone is interested, http://en.wikipedia.org/wiki/Reverse_Polish_notation)
def evaluate(arr)
arr.each_with_index {|ele, index| index-1 if x=="+"}
end
I would like to move operator like =, - and * by one index so it will return 50 - 30. Thanks!
arr.values_at(0, 2, 1)
#=> [50, "+", 30]
arr.values_at(0, 2, 1).join(" ")
#=> "50 + 30"
I'd do it like this:
arr[-1], arr[-2] = arr[-2], arr[-1]
This swaps the last two elements of the array.
You could create a temporary array:
def evaluate(arr)
temp = arr.dup
arr.each_with_index { |ele, index| temp[index-1] = ele }
temp
end
Another way is to repeat the array twice and grab the elements at indices 1-3:
(arr * 2)[1..3]
This doesn't answer your question directly, but if you're looking to implement RPN in Ruby, you don't need to do any array munging. You can iterate your ops and maintain your value stack directly:
def rpn(*ops)
stack = []
ops.each do |op|
case op
when Numeric
stack.push op
else
value = stack.pop
receiver = stack.pop
stack.push receiver.send(op, value)
end
end
stack.first
end
puts rpn 5, 1, 2, "+", 4, "*", "+", 3, "-" # => 14
The fun thing is that since this is Ruby, operations are just messages you send to the receiver, so you can do things beyond basic math - for example, to get the greatest common denominator of two numbers, Ruby provides the "gcd" method, which you can feed into your RPN parser!
puts rpn 64, 24, "gcd" # => 8

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

What is clearest way to add up all elements in 2 dimensional array by position using Ruby?

What is the clearest and most efficient way to add all the elements in 2D array by their position in Ruby. Example:
2darray = [[1,2,3],[1,2,3]]
result = [2,4,6]
I have the following code
def sum_elements_by_position(array)
total_elements = array.length
result = []
for i in 0...array.first.length
n = 0
array.each { |subarray| n += subarray[i] }
result << n
end
result
end
Assumptions: All primary elements are of the same length
For bonus points it would be great to see a solution that works primary elements of an arbitrary length
You can zip the first row with the rest of them and then do the sum:
def sum_elements_by_position(array)
array[0].zip(*array[1..-1]).map do |col|
col.inject(:+)
end
end
Here's a solution addressing when the rows aren't the same length.
def sum_cols arr
arr.reduce( [] ) do |res,row|
row.each_with_index { |e,i| res[i] ||= 0; res[i] += e }
res
end
end
irb> sum_cols [ [0,1,2], [3,4], [5,6,7,8] ]
=> [8, 11, 9, 8]
#oldergod suggested using zip based on the longest row, but finding the longest row and rejecting nils has a cost. I benchmarked the following against the above method using the example array above and found the reduce+each_with_index method more than 30% faster:
def sum_cols_using_zip arr
max_len = arr.map(&:size).max
([0] * max_len).zip(*arr).map do |col|
col.compact.inject(:+)
end
end
I'd do this:
a.transpose.map {|x| x.reduce(:+)}
Clean, simple, flexible. The .transpose turns this
[[1,2,3,4],[2,3,4,5],[3,4,5,6]]
into this
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
and then .map applies .reduce to each subarray. And .reduce, in turn, aggregates the subvalues by adding them. Or, more precisely, by applying the + method to them.
I highly recommend reading the doc for these functions until you fully understand this example, as it's a pretty good succinct demonstration of how to think in a Rubyish way!

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]]

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