Ruby: How to Iterate Over a Range, But in Set Increments

Ruby: How to iterate over a range, but in set increments?

See http://ruby-doc.org/core/classes/Range.html#M000695 for the full API.

Basically you use the step() method. For example:

(10..100).step(10) do |n|
# n = 10
# n = 20
# n = 30
# ...
end

how implement a function to iterate over if its passed a Range

One technique, that changes very little of this implementation, would be to convert your range to an array.

module Enumerable
def my_each
return to_enum :my_each unless block_given?

index = 0
while index < size
if is_a? Array
yield self[index]
elsif is_a? Hash
yield keys[index], self[keys[index]]
elsif is_a? Range
yield to_a[index]
end
index += 1
end
end
end

r_list = (1..10)
puts r_list.my_each { |number| puts number }

Result:

1
2
3
4
5
6
7
8
9
10

How to change the amount a for loop increments in Ruby?

You want to use step

(0..9).step(3) do |i|
puts i
end

Iterate set covered by cross-product of ranges in ruby

There are two orthogonals tasks, try not to to mix them up so the code remains modular.

  1. How to build a cartesian product of N arrays.

  2. How to filter the product and count.

Use Array#product to get the cartesian product:

xs = [0..1, 0..1, 0..1].map(&:to_a)
xss = xs[0].product(*xs[1..-1]) # or xs.first.product(*xs.drop(1))
#=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], ..., [1, 1, 0], [1, 1, 1]]

And now do the filter and couting:

xss.count { |x, y, z| x == 1 && z == 0 }
#=> 2

This is slightly uglier that it should, that's because we'd need the classmethod Array::product instead of the method Array#product. No problem, let's add it into our extensions module and finally write:

Array.product(0..1, 0..1, 0..1).count { |x, y, z| x == 1 && z == 0 }
#=> 2

How do I effectively iterate over a large records and create a new list?

Right now you are loading all the documents in memory and doing a ton of N+1 queries.

Below are some ideas. The code is not tested at all.

So, if we are talking about more that 1k records then you can use find_each.

Note: This method is only intended to use for batch processing of large amounts of records that wouldn’t fit in memory all at once. If you just need to loop over less than 1000 records, it’s probably better just to use the regular find methods.

Then, inside that loop you are doing N+1. It's best to create some associations and eager_load them.

# Example
has_many :reconciled, -> { where(status: true) }, class_name: 'PrimaryFile'
has_many :unreconciled, -> { where(status: false) }, class_name: 'PrimaryFile'

And finally (untested !):

files = []
Document.includes(:reconciled, :unreconciled).find_each do |doc|
reconciled = doc.reconciled.map(&:amount_credit).sum // for eager loading
unreconciled = doc.unreconciled.map(&:amount_credit).sum

files.push({...})
end

You could also eager_load just :primary_files and get the reconciled and unreconciled by filtering the doc.primary_files array to get the sum. This way you eager_load just one relation instead of 3. It's up to you.

Document.includes(:primary_files).find_each do |doc|
reconciled = doc.primary_files.select { |pf| pf.active }.sum(&:amount_credit)
...
end

Make sure when you run the loop you have NO N+1 queries.

How to iterate over all but first element of an enumerable

How about this?

[1,2,3].drop(1).each {|x| puts x }
# >> 2
# >> 3

Here's how you can continue walking the iterator

a = [1,2,3]

b = a.each # => #<Enumerator: [1, 2, 3]:each>
b.next # skip first one

loop do
c = b.next
puts c
end
# >> 2
# >> 3

A better method to iterate over an array and object attributes?

def mymethod
params = {'foo'=>:blue,'bar'=>:red,'jaa'=>:yellow}

params.each do |k,v|
my_object.send(v).push(collect_method(k))
end
end

where collect_method is another method in Myobject class

What is a stable way to iterate on a range with custom step?

Rust 1.28+

Iterator::step_by is now stable:

fn main() {
for i in (0..100).step_by(2) {
println!("{}", i);
}
}

Rust 1.1+

You can always write it out the old-fashioned way:

fn main() {
let mut i = 0;
while i < 100 {
println!("i: {}", i);
i += 2;
}
}

Which can then be abstracted:

use std::ops::Add;

fn step_by<T, F>(start: T, end_exclusive: T, step: T, mut body: F)
where
T: Add<Output = T> + PartialOrd + Copy,
F: FnMut(T),
{
let mut i = start;
while i < end_exclusive {
body(i);
i = i + step;
}
}

fn main() {
step_by(0, 100, 2, |i| {
println!("i: {}", i);
})
}

Interesting historical side note, I believe that originally all the looping was done with closures like this, before iterators became extremely prevalent.

You can then take this and make it into an iterator:

use std::ops::Add;

struct StepBy<T> {
start: T,
end_exclusive: T,
step: T,
}

impl<T> StepBy<T> {
fn new(start: T, end_exclusive: T, step: T) -> Self {
Self {
start,
end_exclusive,
step,
}
}
}

impl<T> Iterator for StepBy<T>
where
T: Add<Output = T> + PartialOrd + Copy,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.start < self.end_exclusive {
let v = self.start;
self.start = self.start + self.step;
Some(v)
} else {
None
}
}
}

fn main() {
for i in StepBy::new(0, 100, 2) {
println!("i: {}", i);
}
}

See also:

  • How can I add new methods to Iterator?

Best way to iterate over a sequence and change one object at a time (Ruby)

Use Array#each:

self.answers.each {|a| a.some_attr = some_val if a.some_criteria}



Related Topics



Leave a reply



Submit