How to modify an array subset in Ruby iteratively? - ruby

I know the idiomatic way to do a for loop in Ruby is to use an Enumerator like .each, but I'm running into a problem: I'd like to iterate over a subset of an Array and modify those elements. Calling .map! with a subset like ary[0..2] or .slice(0..2) doesn't seem to do it; presumably because that slicing operator is creating a new Array?
Desired behavior with for instead of iterator:
iter_ind = [2,3,4]
my_ary = [1,3,5,7,9,11]
for j in iter_ind
my_ary[j] = my_ary[j] + 1
# some other stuff like an exchange operation maybe
end
=> [1, 3, 6, 8, 10, 11]
Things that don't work:
irb(main):032:0> ar[2..4].map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):033:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):034:0> ar.slice(2..4).map! {|el| el = el+1}
=> [6, 8, 10]
irb(main):035:0> ar
=> [1, 3, 5, 7, 9, 11]
irb(main):036:0> ar[2..4].collect! {|el| el = el+1}
=> [6, 8, 10]
irb(main):037:0> ar
=> [1, 3, 5, 7, 9, 11]

Try this.
In example below I implemented something that could be named map_with_index. each_with_index if no block given returns iterator. I use it to map our array.
ary = [1, 3, 5, 7, 9, 11]
ary.each_with_index.map { |elem, index| index.between?(2, 4) ? elem += 1 : elem }
# => [1, 3, 6, 8, 10, 11]

You may also try the following:
?> ary = [1, 3, 5, 7, 9, 11]
=> [1, 3, 5, 7, 9, 11]
?> ary.map!.with_index {|item, index| index.between?(2, 4) ? item += 1 : item}
=> [1, 3, 6, 8, 10, 11]
?> ary
=> [1, 3, 6, 8, 10, 11]

You could use Array#each_index if you don't mind referencing the array twice:
ary = [1, 3, 5, 7, 9, 11]
ary.each_index { |i| ary[i] += 1 if i.between? 2, 4 }
#=> [1, 3, 6, 8, 10, 11]
Or if you don't want to iterate the whole array, this would work, too:
ary = [1, 3, 5, 7, 9, 11]
ary[2..4] = ary[2..4].map { |el| el + 1 }
ary
#=> [1, 3, 6, 8, 10, 11]

Related

Directly convert splat to set

I'm using the below code to generate a set from a range:
my_set = *(1..10).to_set
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
As you can see, instead of getting a set, I get an array.
The statement works if I split it into two lines:
my_set = *(1..10)
my_set = my_set.to_set
# => #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
How can I get it to work as expected in one line?
You already had a set. Splatting it (*) gave you the array. Just don't splat:
my_set = (1..10).to_set # => #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Set.new works as well with ranges:
Set.new(1..10)
=> #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Just for fun, if you want to use splat operator (*):
my_set = (_ = *(1..10)).to_set
=> #<Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}>
Note, that you should use assignment if you want to splat range.

ruby find the index of the next available number

I have a find_num method that returns the index of a specified number in an ordered array, e.g.
find_num(6, [1, 4, 6, 9, 13]) #=> 2
however my spec also requires that if the number is not available it finds the position of the next highest number so ...
find_num(8, [1, 4, 6, 9, 13]) #=> 3
as 9 is the next available number.
Having trouble implementing this... I have thought of iterating through the whole array but I am told to take into account the array could be large...
You can pass a block to index and it ...
[...] returns the index of the first object for which the block returns true. Returns nil if no match is found.
Examples:
[1, 4, 6, 9, 13].index { |n| n >= 6 } #=> 2
[1, 4, 6, 9, 13].index { |n| n >= 8 } #=> 3
[1, 4, 6, 9, 13].index { |n| n >= 15 } #=> nil
Because this requires the array to be ordered, you can also use bsearch_index which performs a binary search.
you can also find a index of any element in array like this.
2.1.8 :040 > [1, 4, 6, 9, 13].index(6)
=> 2
2.1.8 :041 > [1, 4, 6, 9, 13].index(15)
=> nil
def find_num(n,a)
a.each_with_index.to_a.sort_by(&:first).find { |nbr,_| nbr >= n }.last
end
find_num(6, [1, 4, 6, 9, 13])
#=> 2
find_num(8, [1, 4, 6, 9, 13]) #=> 3
#=> 3
The steps for
n = 8
a = [1, 4, 6, 9, 13]
are as follows.
b = a.each_with_index.to_a
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
c = b.sort_by(&:first)
#=> [[1, 0], [4, 1], [6, 2], [9, 3], [13, 4]]
d = c.find { |nbr,_| nbr >= n }
#=> [9, 3]
d.last
#=> 3

Ruby code to merge two arrays not working

nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
def mergeArrays (ar1, ar2)
result = (ar1 << ar2).flatten!
require 'pp'
pp %w(result)
end
As simple as this. I am trying to merge these two arrays and display the result. I am also brand-brand new to Ruby. This is the first function I am writing in this language. Trying to learn here. Also how can I remove the duplicates?
It would help if you give example inputs and outputs so we know exactly what you want. When you use the word "merge", I think you actually just want to add the arrays together:
ar1 = [1, 2, 3]
ar2 = [3, 4, 5]
ar3 = ar1 + ar2 # => [1, 2, 3, 3, 4, 5]
Now if you want to remove duplicates, use Array#uniq:
ar4 = ar3.uniq # => [1, 2, 3, 4, 5]
There is no need to write a method to do any of this since the Ruby Array class already supports it. You should skim through the documentation of the Array class to learn more things you can do with arrays.
What do you mean 'not working'?
Similar questions have been asked here:
Array Merge (Union)
You have two options: the pipe operator (a1 | a2) or concatenate-and-uniq ((a1 + a2).uniq).
Also be careful about using <<, this will modify the original variable, concatenating ar2 onto the end of the original ar1.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
result = (nums1<< nums2).flatten!
nums1
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
nums2
=> [5, 6, 7, 8, 9]
result
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
Additionally- just another Ruby tip, you do not need the destructive flatten! with ! versus the regular flatten. The regular flatten method will return a new Array, which you assign to result in your case. flatten! will flatten self in place, altering whatever Array it's called upon, rather than returning a new array.
You can merge Arrays using '+' operator and you can ignore the duplicated values using .uniq
>> nums1 = Array[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> nums2 = Array[5, 6, 7, 8, 9]
=> [5, 6, 7, 8, 9]
>> def mergeArrays (nums1, nums2)
>> result = (nums1 + nums2).uniq
>> end
=> :mergeArrays
>> mergeArrays(nums1,nums2)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
p nums1.concat(nums2).uniq

Ruby, perform operation on an array and return the new array, aswell as "changes"

I am looking for a way to perform a certain operation (for instance delete_if) on an array and return both the deleted elements, and the remaining elements.
For example
a = [1,2,3,4,5,6,7,8,9,10]
a.delete_if {|x| x.even? } #=> [[1, 3, 5, 7, 9]]
But what I am looking for is something like
a = [1,2,3,4,5,6,7,8,9,10]
a.some_operation #=> [[1,3,5,7,9],[2,4,6,8,10]]
How would I go about doing this?
Using Enumerable#partition:
a = [1,2,3,4,5,6,7,8,9,10]
a.partition &:even?
# => [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
The first element of the Enumerable#partition return value contains the elements that are evaluated to true in the block. So you need to use odd? to get what you want.
a.partition &:odd?
# => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]
You might be looking for something like this:
a = [1,2,3,4,5,6,7,8,9,10]
a.group_by { |x| x.even? }.values
#=> [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

how reverse a matrix object in ruby 1.9?

I am a beginner with Ruby. So I searched how reverse a matrix
1, 2, 3, 4 8, 9, 10, 11
4, 5, 6, 7 => 4, 5, 6, 7
8, 9, 10, 11 1, 2, 3, 4
I have tried:
require 'matrix'
m = Matrix[ [1,2,3,4],[4,5,6,7], [8,9,10,11] ]
m_rev = Matrix.build(m.row_size, m.column_size){|row|
m.row_size.downto(0){|i|
row = m.row(i)
}
}
Try: Matrix[*m.to_a.reverse]:
m = Matrix[ [1,2,3,4], [5,6,7,8], [9,10,11,12] ]
#=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
r = Matrix[*m.to_a.reverse]
#=> Matrix[[9, 10, 11, 12], [5, 6, 7, 8], [1, 2, 3, 4]]
Do not miss the *.
You can do something like:
m_rev = Matrix.rows m.to_a.reverse
You were on the right track, except that Matrix.build iterates over both rows and columns:
m_rev = Matrix.build(m.row_size, m.column_size){|row, column|
m[-row-1, column]
}
Simpler and faster:
m_rev = Matrix.rows(m.to_a.reverse)
# or
m_rev = Matrix[*m.to_a.reverse]

Resources