I have:
array = [1, 4, -1, 3, 2]
I want a new array that follows the following logic:
First element is located at index 0, so it is 1.
Second element is located at index 1 (because value for index 0 was 1).
Third element is located at index 4, so it is 2.
And so on until the loop meets value -1, which is the last value, and it should brake.
The new array should be:
[1, 4, 2, -1]
I have:
def task(a)
array = []
a.each_with_index do |v, i|
result = a[i]
until a[i] == -1
array << a[result]
end
end
puts result
end
As others say, you need to change the index in your loop. Also, if you want -1 in the result, you should exit at bottom. And with_index will give you indices in order, which is not what you want here. This will do what you want:
def task(a)
i = 0
array = []
begin
i = a[i]
array << i
end until i == -1
array
end
p task([1, 4, -1, 3, 2])
# => [1, 4, 2, -1]
until a[i] == -1
array << a[result]
end
This code is looping eternally - there is nothing to change i .
As discussed in the comments, you are looping through the array which is not what you require.
You could use a recursive method to handle jumping from one element to another based on previous value. Consider the following:
arr = [1, 4, -1, 3, 2]
def task(arr, n=0, result=[])
if arr[n] == -1
return result + [-1]
end
r = arr[n]
task(arr, r, result + [r])
end
puts task(arr)
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
first_element = input_array.first
last_element = input_array[last_valid_index]
middle_elements = (1..last_valid_index).map { |i| input_array[input_array[i-1]]}
output_array = [first_element] + middle_elements + [last_element]
p output_array
# => [1, 4, 2, -1]
you could to most of it on one line like so, but I think the more verbose version is more self documenting.
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
output_array = [input_array.first] + (1..last_valid_index).map { |i| input_array[input_array[i-1]]} + [input_array[last_valid_index]]
p output_array
# => [1, 4, 2, -1]
I'd suggest this option, just to avoid infinite loops or index out range:
i, ary = 0, [array[0]]
array.size.times do
break if array[i] == -1 or array[i] > array.size - 1
i = array[i]
ary << array[i]
end
ary #=> [1, 4, 2, -1]
An infinite loop happens for example when array = [1, 4, -1, 0, 3].
Index out of range can happen when array = [1, 4, 6, 3, 2]
I'm iterating over permutations of a list (18 items) like this:
List = [item0..item18] # (unpredictable)
Permutation_size = 7
Start_at = 200_000_000
for item, i in List.repeated_permutation(Permutation_size).each_with_index
next if i < Start_at
# do stuff
end
Start_at is used to resume from a previously saved state so it's always different but it takes almost 200s to reach 200 million so I'm wondering if there is a faster way to skip multiple iterations or start at iteration n (converting the enumerator to an array takes even longer). If not, a way to create a custom repeated_permutation(n).each_with_index (that yields results in the same order) would also be appreciated.
Feel free to redirect me to an existing answer (I haven't found any)
PS. (what I had come up with)
class Array
def rep_per_with_index len, start_at = 0
b = size
raise 'btl' if b > 36
counter = [0]*len
# counter = (start_at.to_s b).split('').map {|i| '0123456789'.include?(i) ? i.to_i : (i.ord - 87)} #this is weird, your way is way faster
start_at.to_s(b).chars.map {|i| i.to_i b}
counter.unshift *[0]*(len - counter.length)
counter.reverse!
i = start_at
Enumerator.new do |y|
loop do
y << [counter.reverse.map {|i| self[i]}, i]
i += 1
counter[0] += 1
counter.each_with_index do |v, i|
if v >= b
if i == len - 1
raise StopIteration
else
counter[i] = 0
counter[i + 1] += 1
end
else
break
end
end
end
end
end
end
I first construct a helper method, change_base, with three arguments:
off, the base-10 offset into the sequence of repeated permutations of the given array arr,
m, a number system base; and
p, the permutation size.
The method performs three steps to construct an array off_m:
converts off to base m (radix m);
separates the digits of the base m value into an array; and
if necessary, pads the array with leading 0s to make it of size p.
By setting m = arr.size, each digit of off_m is an offset into arr, so off_m maps the base-10 offset to a unique permutation of size p.
def change_base(m, p, off)
arr = off.to_s(m).chars.map { |c| c.to_i(m) }
arr.unshift(*[0]*(p-arr.size))
end
Some examples:
change_base(16, 2, 32)
#=> [2, 0]
change_base(16, 3, 255)
#=> [0, 15, 15]
change_base(36, 4, 859243)
#=> [18, 14, 35, 31]
18*36**3 + 14*36**2 + 35*36**1 + 31
#=> 859243
This implementation of change_base requires that m <= 36. I assume that will be sufficient, but algorithms are available to convert base-10 numbers to numbers with arbitrarily-large bases.
We now construct a method which accepts the given array, arr, the size of each permutation, p and a given base-10 offset into the sequence of permutations. The method returns a permutation, namely, an array of size p whose elements are elements of arr.
def offset_to_perm(arr, p, off)
arr.values_at(*change_base(arr.size, p, off))
end
We can now try this with an example.
arr = (0..3).to_a
p = 2
(arr.size**p).times do |off|
print "perm for off = "
print " " if off < 10
print "#{off}: "
p offset_to_perm(arr, p, off)
end
perm for off = 0: [0, 0]
perm for off = 1: [0, 1]
perm for off = 2: [0, 2]
perm for off = 3: [0, 3]
perm for off = 4: [0, 1]
perm for off = 5: [1, 1]
perm for off = 6: [2, 1]
perm for off = 7: [3, 1]
perm for off = 8: [0, 2]
perm for off = 9: [1, 2]
perm for off = 10: [2, 2]
perm for off = 11: [3, 2]
perm for off = 12: [0, 3]
perm for off = 13: [1, 3]
perm for off = 14: [2, 3]
perm for off = 15: [3, 3]
If we wish to begin at, say, offset 5, we can write:
i = 5
p offset_to_perm(arr, p, i)
[1, 1]
i = i.next #=> 6
p offset_to_perm(arr, p, i)
[2, 1]
...
Class Image initializes with an array of 0's and 1's. I have method transform, such that
[[0,0,0],
[0,1,0],
[0,0,0]]
returns
[[0,1,0],
[1,1,1],
[0,1,0]]
I want to implement method blur(n), which iterates n times with transform, such calling blur(2) with
[[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]]
returns
[[0,0,0,0,1,0,0,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,1,1,1,1,1,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,0,0,1,0,0,0,0]]
I'm trying to use transform iteratively to achieve this, but I'm getting undefined method 'map' for #<Context::Image:0x000000012eb020> when calling blur with an instance of Image. How can I iterate over each successive transformation, such that blur returns the latest version with the maximum n transformations?
class Image
attr_accessor :array
def initialize(array)
self.array = array
end
def output_image
self.array.each do |item|
puts item.join
end
end
def transform #changes adjacent a 1's adjacent 0's into 1
cloned = self.array.map(&:clone)
#scan original array for 1; map crosses into clone if found
self.array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def blur(n) #should call transform iteratively n times
blurred = Image.new(self)
n.times do
blurred = blurred.transform
end
blurred
end
end
You could use the Matrix class.
require 'matrix'
class Matrix
def el(r,c)
if r < 0 || r >= row_count || c < 0 || c >= column_count
0
else
self[r,c]
end
end
def transform
Matrix.build(row_count, column_count) { |r,c|
[el(r,c), el(r-1,c), el(r+1,c), el(r,c-1), el(r,c+1)].max }
end
end
Given a row-column pair, r, c, the helper method el returns 0 if the row or column is outside the bounds of the matrix and the value at [r,c] otherwise.
nrows = 5
ncols = 5
m = Matrix.build(nrows, ncols) { |r,c| (r==nrows/2 && c==ncols/2) ? 1 : 0 }
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
# Matrix[[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]
m.to_a
#=> [[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]
map is a method available to an Array, but not to your custom class Image.
I suggest calling map on your instance variable #array instead. Then, when your transforms are completed, create a new Image instance with that transformed array.
Below is an example of code that should work. Note that transform and blur take input arrays as parameters, so they do not rely on any instance state. Therefore, I've made them class methods instead of instance methods. This allows your users to use them without having to create an instance, if all they want to do is the array transformation. It also makes those methods easy to extract to a module in future refactorings. I've added an instance method, blurred_image, which applies the transformation to the instance and returns a new Image instance.
def self.transform(input_array) #changes adjacent a 1's adjacent 0's into 1
cloned = input_array.map(&:clone)
#scan original array for 1; map crosses into clone if found
input_array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def self.blur(input_array, transform_count) #should call transform iteratively n times
blurred = input_array
transform_count.times { blurred = transform(blurred) }
Image.new(blurred)
end
def blurred_image(transform_count)
self.class.new(self.class.blur(array, transform_count))
end
Is there a Ruby idiom for popping items from an array while a condition is true, and returning the collection?
I.e,
# Would pop all negative numbers from the end of 'array' and place them into 'result'.
result = array.pop {|i| i < 0}
From what I can tell, something like the above doesn't exist.
I'm currently using
result = []
while array.last < 0 do
result << array.pop
end
Maybe you are looking for take_while?
array = [-1, -2, 0, 34, 42, -8, -4]
result = array.reverse.take_while { |x| x < 0 }
result would be [-8, -4].
To get the original result back you could use drop_while instead.
result = array.reverse.drop_while { |x| x < 0 }.reverse
result would be [-1, -2, 0, 34, 42] in this case.
You could write it yourself:
class Array
def pop_while(&block)
result = []
while not self.empty? and yield(self.last)
result << self.pop
end
return result
end
end
result = array.pop_while { |i| i < 0 }
In case your looking for a solution to pop all items that satisfy a condition, consider a select followed by a delete_if, e.g.
x = [*-10..10].sample(10)
# [-9, -2, -8, 0, 7, 9, -1, 10, -10, 3]
neg = x.select {|i| i < 0}
# [-9, -2, -8, -1, -10]
pos = x.delete_if {|i| i < 0}
# [0, 7, 9, 10, 3]
# note that `delete_if` modifies x
# so at this point `pos == x`
This is what I want to do
a = [1, 2, 3, 4]
a[1] = 0
a[2] = 0
one way to do this is to loop
(1..2).each { |x| x = 0 }
is there a way to do this somehow with ranges or splats? something like
a[(1..2)] = 0
a = [1, 2, 3, 4]
a[1..2] = [0] * 2
p a #[1, 0, 0, 4]
You can't just type a[1..2] = 0 at line 2, cause the array a will become [1, 0, 4]
Or, with Array#fill
a.fill(0, 1..2)
With range
ary = [1, 2, 3, 4]
ary[1..2] = [0,0]
Using [start, length]
a = [1,2,3,4]
a[1,2] = [0,0]