Divide array into subarray - arrays

I want to create two sub-arrays from this array:
a = [0, 1, 2, 3, 4, 5, 6]
This array will not always contain the same number of elements because it depends on the user input.
For example, in some occasions it'll be:
a = [0, 5]
or:
a = [5, 6, 4]
I want to divide the array into two subarrays. The first one will contain numbers from 1 to 4 (inclusive) and the second one will contain 0, 5 and 6.
In the first example, it will be:
a = [0, 1, 2, 3, 4, 5, 6]
sub_array1 = [1, 2, 3, 4]
sub_array2 = [0, 5, 6]
In the second:
a = [0, 5]
sub_array1 = []
sub_array2 = [5]
In the third:
a = [5, 6, 4]
sub_array1 = [4]
sub_array2 = [5, 6]
and so on, depending on the user input.
How can I do this?

First thing that comes to mind is Enumerable#partition.
sub_array1, sub_array2 = [0,1,2,3,4,5,6].partition {|x| (1..4).include? x }
=> [[1,2,3,4], [0,5,6]]
if you have two conditions (I mean if 0,5,6 are an actual condition and not the excluded set) I think that a double iteration wouldn't hurt
a = [0,1,2,3,4,5,6]
sub_array1 = a.select { |x| (1..4).include? x }
sub_array2 = a.select { |x| [0,5,6].include? x }

You can try something like this:
[0,1,2,3,4,5,6].group_by{|x| [0,5,6].include? x}
The result will be a hash:
{true=>[0, 5, 6], false=>[1, 2, 3, 4]}
In the second case:
[0,5].group_by{|x| [0,5,6].include? x}
The result will be:
{true=>[0, 5]}

Related

Sorting an array according to an element inside this array

I need to order my array according to an element of it, the reference element can vary.
For example, I would like the 3 to become the first element of the array and the 1, 2 to be put at the end.
array = [1, 2, 3, 4, 5, 6]
new_array = [3, 4, 5, 6, 1, 2]
The element may vary. If I start from 5, the behavior must be the same: the elements that precede are placed at the end, so I will have :
new_array = [5, 6, 1, 2, 3, 4]
If I understand correctly you want to rotate the array.
array
# [1, 2, 3, 4, 5, 6]
array.rotate(2) # or array.rotate(array.index(3))
# [3, 4, 5, 6, 1, 2]
https://apidock.com/ruby/v2_5_5/Array/rotate
Definitely use #rotate for this in actual use, but as an alternative, you could do something like #shift and #push until the desired element is at the beginning of the array.
def rotate(arr, elem)
arr2 = arr.clone
arr2.push(arr2.shift) until arr2.first == elem
arr2
end
irb(main):026:0> arr = [1, 2, 3, 4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
irb(main):027:0> rotate(arr, 3)
=> [3, 4, 5, 6, 1, 2]
irb(main):028:0> arr
=> [1, 2, 3, 4, 5, 6]
Clearly, if elem is not in arr, this will run forever. You could implement some kind of check to ensure this doesn't happen, but that's just one reason you shouldn't actually do this as anything other than a learning exercise.
One approach would be to find the index of elem in arr and shift/push that many times. The &. operator may be useful in that situation to deal with the possibility of not finding elem in arr.

Remove elements from an array into another array (Ruby)

I'm new here and new to coding and I can use some help with a problem I'm trying to solve.
I'm trying to remove all integers that are less than 5 from array
a = [1, 2, 3, 4, 5, 6] and put them into a new array b = [], and then print out the b array.
I've done many Google searches but I can't find anything that helps.
I'm starting to think this is not possible.
Please help!
Thanks
a = [1, 2, 3, 4, 5, 6]
b, a = a.partition { |i| i < 5 }
#=> [[1, 2, 3, 4], [5, 6]]
b #=> [1, 2, 3, 4]
a #=> [5, 6]
See Enumerable#partition.
a = [1, 2, 3, 4, 5, 6]
b = a.select { |i| i < 5 } # [1, 2, 3, 4]
a = a - b # [5, 6]
To actually remove elements from a while putting them in an existing array b, you could use reject!:
a = [1, 2, 3, 4, 5, 6]
b = []
a.reject! { |i| b << i if i < 5 }
a #=> [5, 6]
b #=> [1, 2, 3, 4]
If i < 5 evaluates to true, b << i puts that element in b and returns a truthy result which causes reject! to remove it from a.
Likewise, if i < 5 evaluates to false, b << i is skipped, the block returns a falsy result and that element remains in a.

Ruby save modified array in a variable without it changing the original array

I'd like to save in two variables the values of an array excluding the first and last elements.
For example:
prices = [9, 3, 5, 2, 1]
The elements I need are:
prices_excl_first = [3, 5, 2, 1]
prices_excl_last = [9, 3, 5, 2]
I figured out how to remove an element from an array a few ways, including slicing off the value by passing its index to the slice method like so:
first_price = prices.slice(0)
last_price = prices.slice(-1)
We could then save the modified arrays into variables:
array_except_first_price = prices.delete(first_price) #=> [3, 5, 2, 1]
array_except_last_index = prices.delete(last_price) #=> [3, 5, 2]
There are two problems with this:
array_except_last_index doesn't contain the first element now
I still need access to the full, original array prices later
So essentially, how can I just temporarily modify the elements in the array when necessary in the problem?
Slicing and dropping elements from array permanently affect the array.
Ruby has first and last to copy just the first and last elements.
Ask for the first and last prices.size-1 elements.
prices = [9, 3, 5, 2, 1]
except_first = prices.last(prices.size-1)
except_last = prices.first(prices.size-1)
#Schwern's answer is probably the best you can get. Here's the second best:
prices = [9, 3, 5, 2, 1]
prices[1..-1] # => [3, 5, 2, 1]
prices[0..-2] # => [9, 3, 5, 2]
Or drop/take (which more closely map to the wording of your question).
prices.drop(1) # => [3, 5, 2, 1]
prices.take(prices.size-1) # => [9, 3, 5, 2]
You could use each_cons:
a, b = prices.each_cons(prices.size - 1).to_a
a #=> [9, 3, 5, 2]
b #=> [3, 5, 2, 1]
Splat it.
*a, d = prices
c, *b = prices
a #=> [9, 3, 5, 2]
b #=> [3, 5, 2, 1]
You can use dup to duplicate the array before performing destructive operations.
prices = [9, 3, 5, 2, 1]
except_first = prices.dup
except_first.delete_at 0
except_last = prices.dup
except_last.delete_at -1
This does end up duplicating the array a couple of times. If you're dealing with large arrays, this may be a problem.

How to select the first increasing values from Ruby array without enumerating every value?

Imagine one has an array such as:
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
And one wishes to create an array consisting of the first n elements, starting with the first element in the array, that are a monotonic sequence increasing by one. Given a above, that array would be [0, 1, 2, 3, 4].
One could use slice_when, such as:
a.slice_when { |a, b| a != b - 1 }.first
The drawback of this approach is that slice_when continues to iterate over the array elements 2, 5, 1, and so on, until the end. In this case, iterating over the remaining values is useless, since one really just wants the first slice.
What is the elegant way to express this in Ruby, that ceases iterating once the first increasing sequence is selected?
How about lazy evaluation?
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
a.lazy.slice_when { |a, b| a != b - 1 }.first
=> [0, 1, 2, 3, 4]
Enumerable#lazy
You could write the following.
def stairstep(arr)
return [] if arr.empty?
enum = arr.first.step
arr.take_while { |x| x == enum.next }
end
stairstep [1, 2, 3, 4, 6, 5, 1, 7, 6, 4, 5]
#=> [1, 2, 3, 4]
Perhaps something like this:
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
(a.size - 1).times { |i| break a[0..i] if a[i] > a[i + 1] }
#=> [0,1,2,3,4]

Finding the first combination of two integers in an array whose latter element appears the earliest and sum matches a given value

I have array and sum_of_two:
array = [10, 5, 1, 9, 7, 8, 2, 4, 6, 9, 3, 2, 1, 4, 8, 7, 5]
sum_of_two = 10
I'm trying to find the combination of two integers in array whose latter element of the two appears the earliest among those of such combinations whose sum equals sum_of_two. For example, both [5, 5] and [1, 9] are candidates for such combinations, but 9 of [1, 9] (which appears later than 1 in array) appears earlier than the second 5 of [5, 5] (which is the last element in array). So I would like to return [1, 9].
I tried using combination and find:
array.combination(2).find{|x,y| x + y == sum_of_two} #=> [5, 5]
However, it returns a combination of the first integer in the array, 5 , and another integer further along the array, also 5.
If I use find_all instead of find, I get all combinations of two integers that add up to sum_of_two:
array.combination(2).find_all{|x,y| x + y == sum_of_two}
#=> [[5, 5], [1, 9], [1, 9], [9, 1], [7, 3], [8, 2], [8, 2], [2, 8], [4, 6], [6, 4], [9, 1], [3, 7], [2, 8]]
But then I'm not sure how to get the first one.
I would use Set (which would be a bit more efficient than using Array#include?) and do something like this:
array = [10, 5, 1, 9, 7, 8, 2, 4, 6, 9, 3, 2, 1, 4, 8, 7, 5]
sum_of_two = 10
require 'set'
array.each_with_object(Set.new) do |element, set|
if set.include?(sum_of_two - element)
break [sum_of_two - element, element]
else
set << element
end
end
#=> [1, 9]
x = array.find.with_index{|e, i| array.first(i).include?(sum_of_two - e)}
[sum_of_two - x, x] # => [1, 9]
Array#combination(n) does not give the elements in the order you want, so you must build the pairs yourself. It's easy if you begin from the second index. A O(n) lazy implementation, and let's call the input xs:
pairs = (1...xs.size).lazy.flat_map { |j| (0...j).lazy.map { |i| [xs[i], xs[j]] } }
first_matching_pair = pairs.detect { |i, j| i + j == 10 }
#=> [1, 9]

Resources