Let's say I have an array of numbers, e.g.
ary = [1, 3, 6, 7, 10, 9, 11, 13, 7, 24]
I would like to split the array between the first point where a smaller number follows a larger one. My output should be:
[[1, 3, 6, 7, 10], [9, 11, 13, 7, 24]]
I've tried slice_when
and it comes quite close:
ary.slice_when { |i, j| i > j }.to_a
#=> [[1, 3, 6, 7, 10], [9, 11, 13], [7, 24]]
But it also splits between 13
and 7
, so I have to join the remaining arrays:
first, *rest = ary.slice_when { |i, j| i > j }.to_a
[first, rest.flatten(1)]
#=> [[1, 3, 6, 7, 10], [9, 11, 13, 7, 24]]
which looks a bit cumbersome. It also seems inefficient to keep comparing items when the match was already found.
I am looking for a general solution based on an arbitrary condition. Having numeric elements and i > j
is just an example.
Is there a better way to approach this?
Example-2: This example uses the splice() method to split the array into chunks of array. This method removes the items from the original array. This method can be used repeatedly to split array of any size.
A Simple solution is to run two loop to split array and check it is possible to split array into two parts such that sum of first_part equal to sum of second_part. Below is the implementation of above idea.
Given an array of n non-negative integers. Choose three indices i.e. (0 <= index_1 <= index_ 2<= index_3 <= n) from the array to make four subsets such that the term sum(0, index_1) – sum(index_1, index_2) + sum(index_2, index_3) – sum(index_3, n) is maximum possible.
There's probably a better way to do this but my first thought is...
break_done = false
ary.slice_when { |i, j| (break_done = i > j) unless break_done }.to_a
#=> [[1, 3, 6, 7, 10], [9, 11, 13, 7, 24]]
I'm not sure you'll find this more elegant, but it prevents the split-and-rejoin maneuver:
def split(ary)
split_done = false
ary.slice_when do |i, j|
!split_done && (split_done = yield(i, j))
end.to_a
end
Usage:
ary = [1, 3, 6, 7, 10, 9, 11, 13, 7, 24]
split(ary){ |i, j| i > j }
#=> [[1, 3, 6, 7, 10], [9, 11, 13, 7, 24]]
Update:
Some may find this variant more readable. #chunk_while
is the inverse of #split_when
and then I just applied De Morgan's Law to the block.
def split(ary)
split_done = false
ary.chunk_while do |i, j|
split_done || !(split_done = yield(i, j))
end.to_a
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With