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

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

Related

Repeating a loop when it reaches the end

I am trying to conceptualize the iteration of two loops
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
While the numbers_array iterates, add_to_array iterates simultaneously adding both elements together at the same time. The caveat is once add_to_array reaches the end, it starts over adding its element to the next index in numbers_array. So at numbers_array[4] we would be adding add_to_array[0] then adding numbers_array[5] to add_to_array[1] and so on. This process would repeat until we reach the end of the numbers_array.
Any input would be greatly appreciated!
You are looking for Enumerable#zip and Enumerable#cycle:
numbers_array = [1,2,3,4,5,6,7,8,9,10]
#⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
add_to_array = [1,2,3,4]
#⇒ [1, 2, 3, 4]
numbers_array.zip(add_to_array.cycle)
#⇒ [[1, 1], [2, 2], [3, 3], [4, 4], [5, 1],
# [6, 2], [7, 3], [8, 4], [9, 1], [10, 2]]
Now do whatever you want with the array returned. E.g. to reduce the zipped result summing elements, map ro Enumerable#sum:
numbers_array.zip(add_to_array.cycle).map(&:sum)
#⇒ [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
It works by using the % operator to cycle through the indexes.
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
numbers_array.map.with_index do |n, i|
n + add_to_array[i % add_to_array.length]
end
A cool method that's similar, if you didn't want to start over at the next array, would be .zip
https://apidock.com/ruby/Array/zip
add_to_array.zip(*numbers_array.each_slice(add_to_array.size)).
map { |a| a.sum { |e| e.to_i } }
#=> [16, 20, 13, 16]
e.to_i is needed to convert nil values to zeros. See NilClass#to_i.
Another option:
numbers_array.map { |e| e + add_to_array.rotate!.last }
# => [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
Drawback: add_to_array is mutated by rotate!

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

How to perform function to subarray inside ruby multidimensional array

I have a multidimensional array in ruby like this one:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
How do I add "1" to each element. For instance, I want to end up with something like this:
a = [[2, 3, 4], [5, 6, 7], [8, 9, 10]]
Thanks in advance!
There might be a slightly more clever one liner but this is fairly clear.
a.map { |ar| ar.map { |e| e + 1 } }
Just for fun :
class Array
def increment
map(&:next)
end
end
#Tada!
a.map(&:increment)
a.map { |xs| xs.map(&:succ) }
#=> [[2, 3, 4], [5, 6, 7], [8, 9, 10]]

How can I interweave items from two arrays?

How can I go from this:
for number in [1,2] do
puts 1+number
puts 2+number
puts 3+number
end
which will return 2,3,4 then 3,4,5 -> 2,3,4,3,4,5. This is just an example, and clearly not the real use.
Instead, I would like it to return 2,3 3,4 4,5 -> 2,3,3,4,4,5. I would like each of the puts to be iterated for each of the possible values of number; In this case 1 and 2 are the two possible values of 'number', before moving on to the next puts.
One way to do this is to create two lists, [2,3,4] and [3,4,5] and then use the zip method to combine them like [2,3,4].zip([3,4,5]) -> [2,3,3,4,4,5].
zip is good. You should also look at each_cons:
1.9.2p290 :006 > [2,3,4].each_cons(2).to_a
=> [[2, 3], [3, 4]]
1.9.2p290 :007 > [2,3,4,5,6].each_cons(2).to_a
=> [[2, 3], [3, 4], [4, 5], [5, 6]]
1.9.2p290 :008 > [2,3,4,5,6].each_cons(3).to_a
=> [[2, 3, 4], [3, 4, 5], [4, 5, 6]]
Because each_cons returns an Enumerator, you can use a block with it, as mentioned in the documentation for it, or convert it to an array using to_a like I did above. That returns the array of arrays, which can be flattened to get a single array:
[2,3,4,5].each_cons(2).to_a.flatten
=> [2, 3, 3, 4, 4, 5]
From the ri docs:
Iterates the given block for each array of consecutive elements. If no
block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) {|a| p a}
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
Maybe not the most readable code but you could use inject on the first range to create an array based on the summed up second range.
(1..3).inject([]){|m,n| (1..2).each{|i| m<<n+i }; m }
=> [2, 3, 3, 4, 4, 5]
This might be a little more readable
res=[]
(1..3).each{|r1| (1..2).each{|r2| res<<r1+r2 } }
[1, 2, 3].each { |i| [1, 2].each { |y| puts i + y } }

Sort hash by key as a reverse order, return hash in Ruby

Can you please help anyone?. I have a hash value as following. I need to sort the hash in reverse order by key value and return as also hash type.
My actual expected output is (hash)
result = {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}
# this is my input
irb(main):096:0> h = {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
# It's not correct
irb(main):095:0> Hash[h.sort]
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
So, i tried this. It's correct but it's return as a array value, i need a return value as Hash.
# It's correct but it's not a hash
irb(main):092:0> arr = h.sort_by { |k,v| k }.reverse
=> [[20111104111221, [4, 5, 6]], [20111104111220, [7, 8, 9]], [20111104110950, [1, 2, 3]]]
Again i tried array to hash conversion.. but it's help.
# It's also not correct.
irb(main):092:0> irb(main):098:0> Hash[*arr.flatten]
=> {5=>6, 20111104111221=>4, 20111104110950=>1, 2=>3, 8=>9, 20111104111220=>7}
You're almost there with h.sort_by..., don't flatten that just feed it to Hash[]:
Hash[h.sort_by { |k,v| -k }]
I also simplified your sorting, negating the number does the same thing as sorting and reversing.
For example:
>> h = {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
=> {20111104111221=>[4, 5, 6], 20111104110950=>[1, 2, 3], 20111104111220=>[7, 8, 9]}
>> Hash[h.sort_by { |k,v| -k }]
=> {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}
Which version of ruby are you using? In 1.8 hashes cannot be ordered, in 1.9 they are ordered based on insertion. Here's more info on that.
Given the variation in how they're handled I wouldn't focus on sorting the hash itself, but on sorting your keys and using them as a reference, something like:
data = {20111104111221=>[4, 5, 6], 20111104111220=>[7, 8, 9], 20111104110950=>[1, 2, 3]}
keys = data.keys.sort.reverse
keys.each do |key|
puts data[key].pretty_inspect
# ... do work ...
end
The debug line is in there just as an example of how to access your values. Hope this helps!

Resources