Cycle through an array in Ruby - ruby-on-rails

I have an array of the form [1,2,3,4,5].
Is it possible to loop through this array and during each iteration get an array where the starting point is the current element and the end point is the element before that?
Like
[1,2,3,4,5]
[2,3,4,5,1]
[3,4,5,1,2]
[4,5,1,2,3]
[5,1,2,3,4]
I am trying with .cycle method of array but it is not giving the expected result.

Check out rotate.
a = [ "a", "b", "c", "d" ]
a.rotate #=> ["b", "c", "d", "a"]

irb(main):005:0> array = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):006:0> array.size.times.map{|i| array.rotate(i)}
=> [[1, 2, 3, 4, 5], [2, 3, 4, 5, 1], [3, 4, 5, 1, 2], [4, 5, 1, 2, 3], [5, 1, 2 , 3, 4]]

Related

How to get even groups in ruby?

Let's say I want to get a certain number of even groups based on a collection of records with varying count. How is this possible?
I'm looking for a method like objects.in_x_even_groups(4)
Group your objects by their index modulo the number of groups.
objects.group_by.with_index { |_, i| i % num_groups }.values
Example:
objects = %w{a b c d e f g h i j k}
objects.group_by.with_index { |_, i| i % 3 }.values
# [["a", "d", "g", "j"], ["b", "e", "h", "k"], ["c", "f", "i"]]
This won't pad undersized groups with nil and it also will interleave your objects. So this won't work if you need consecutive objects to be in the same group.
You are probably looking for the in_groups method. From the docs:
in_groups(number, fill_with = nil)
Splits or iterates over the array in number of groups, padding any remaining slots with fill_with unless it is false.
%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
["1", "2", "3", "4"]
["5", "6", "7", nil]
["8", "9", "10", nil]
I assume:
the elements are to be kept in order;
l-s is to be minimized, where l is the size of the largest group and s is the size of the smallest group; and
group sizes are non-increasing.
l-s will be at most 1.
def group_em(arr, ngroups)
n_per_group, left_over = arr.size.divmod(ngroups)
cum_off = 0
ngroups.times.map do |i|
n = n_per_group + (i < left_over ? 1 : 0)
a = arr[cum_off, n]
cum_off += n
a
end
end
arr = [1, 2, 3, 4, 5, 6, 7]
(1..7).each { |m| puts "ngroups=#{m}: #{group_em(arr, m)}" }
ngroups=1: [[1, 2, 3, 4, 5, 6, 7]]
ngroups=2: [[1, 2, 3, 4], [5, 6, 7]]
ngroups=3: [[1, 2, 3], [4, 5], [6, 7]]
ngroups=4: [[1, 2], [3, 4], [5, 6], [7]]
ngroups=5: [[1, 2], [3, 4], [5], [6], [7]]
ngroups=6: [[1, 2], [3], [4], [5], [6], [7]]
ngroups=7: [[1], [2], [3], [4], [5], [6], [7]]
You're looking for in_groups_of:
https://apidock.com/rails/Array/in_groups_of
array = %w(1 2 3 4 5 6 7 8 9 10)
array.in_groups_of(3) {|group| p group}
=>
["1", "2", "3"]
["4", "5", "6"]
["7", "8", "9"]
["10", nil, nil]

How to compare arrays inside an array with each other in ruby?

[ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
[ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
I need the intersection of each array with all other arrays within an array.
So the array could look like ->
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
The result should look like ->
a = [[3],[3,4,5][4,5]]
Any suggestions?
Look into the combination method.
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6],[1,"a","b"]]
p a.combination(2).map{|x,y| x & y } #=> [[3], [], [1], [4, 5], [], []]
And if you do not want the empty arrays in there:
p a.combination(2).map{|x,y| x & y }.reject(&:empty?) #=> [[3], [1], [4, 5]]
Edit: After seeing some examples what OP actually want here is how I would achieve the desired result:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
def intersect_with_rest(array)
array.size.times.map do
first, *rest = array
array.rotate!
first & rest.flatten
end
end
p intersect_with_rest(original) #=> [[3], [3, 4, 5], [4, 5]]
p original #=> [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
Or:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
result = original.map.with_index do |x,i|
x & (original[0...i]+original[1+i..-1]).flatten
end
p result #=> [[3], [3, 4, 5], [4, 5]]
Yeah, finally I found a solution. Maybe there is a simpler way, but that works for me now..
c = [[1,2,3],[3,4,5],[4,5,6]]
results = [];c.length.times.each {|e| results.push c.rotate(e).combination(2).map {|x, y| x & y}}
results.map{|x, y| y + x}
=> [[3], [3, 4, 5], [4, 5]]
Thanks to #hirolau for the hint. Best regards

Find a value in common in two two-dimensionals arrays Ruby

I have two arrays, and each is two dimensional, I want to take the value of array 1 in position i and try to find the same value in array 2. If they have the same value, the values of array 2 in x position is concatenated in array 1 in position i.
example
Array 1
[[1,2,3,4,5,6],[3,5,7,2,12,5],[a,f,3,d,4,g]]
Array 2
[[b,e,w,1,4,6] , [1,0,8,5,8,9]]
So the new array 1 will be
[[1,2,3,4,5,6,1,0,8,5,8,9],[3,5,7,2,12,5],[a,f,3,d,4,g]]
Any thought.....
Your description of the task isn't correct. You can't
take the value of the array 1 in position i
because array 1 contain arrays itself, so you can take value in position i, j. And search for same values in array 2 in each i on position j. And if this is what you want, here is sample code:
arr_1 = [[1, 2, 3, 4, 5, 6], [3, 5, 7, 2, 12, 5], ['a', 'f', 3, 'd', 4, 'g']]
arr_2 = [['b', 'e', 'w', 1, 4, 6], [1, 0, 8, 5, 8, 9]]
search_i = 0
search_j = 0
search_val = arr_1[search_i][search_j]
arr_2.each_with_index do |val_i, i|
val_i.each_with_index do |val_j, j|
if j == search_j && arr_2[i][j] == search_val
arr_1[search_i].concat(arr_2[i])
end
end
end
p arr_1 # [[1, 2, 3, 4, 5, 6, 1, 0, 8, 5, 8, 9], [3, 5, 7, 2, 12, 5], ["a", "f", 3, "d", 4, "g"]]

How do I repeat certain items in an array based on matched values in another array?

I've been trying for a couple weeks to figure this out, but I'm totally stumped.
I have an array that represents item_id's: [2, 4, 5, 6, 2, 3].
I have another array that represents how many times each item shows up: [1, 1, 3, 3, 2, 5] .
I want to check that all items have been completed so I want to create an array that has the total number of item_id's in it. I will compare that array against a completed items array that will be created as the user completes each item, so, from the example above, the array I'm trying to create is:
[2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
EDIT:
I'm building a workout app, so a user has a workout which has many exercises. Each exercise has one or more sets associated with it. The user completes an exercise when he has completed every set for that exercise, and completes a workout when he completes all exercises for that workout. In this question I'm trying to determine when a user has finished a workout.
EDIT 2:
I wish I could award multiple right answers! Thanks everyone!
Ok, #sameera207 suggested one way, then I will suggest another way (functional style):
arr1 = [2, 4, 5, 6, 2, 3]
arr2 = [1, 1, 3, 3, 2, 5]
arr1.zip(arr2).flat_map { |n1, n2| [n1] * n2 }
item_ids = [2, 4, 5, 6, 2, 3]
counts = [1, 1, 3, 3, 2, 5]
item_ids.zip(counts).map{|item_id,count| [item_id]*count}.flatten
=> [2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
What's going on here? Let's look at it step by step.
zip takes two arrays and "zips" them together element-by-element. I did this to create an array of item_id, count pairs.
item_ids.zip(counts)
=> [[2, 1], [4, 1], [5, 3], [6, 3], [2, 2], [3, 5]]
map takes each element of an array and executes a block. In this case, I'm using the * operator to expand each item_id into an array of count elements.
[1]*3 => [1, 1, 1]
[[2, 1], [4, 1], [5, 3], [6, 3], [2, 2], [3, 5]].map{|item_id,count| [item_id]*count}
=> [[2], [4], [5, 5, 5], [6, 6, 6], [2, 2], [3, 3, 3, 3, 3]]
Finally, flatten takes an array of arrays and "flattens" it down into a 1-dimensional array.
[[2], [4], [5, 5, 5], [6, 6, 6], [2, 2], [3, 3, 3, 3, 3]].flatten
=> [2, 4, 5, 5, 5, 6, 6, 6, 2, 2, 3, 3, 3, 3, 3]
ids = [2, 4, 5, 6, 2, 3]
repeats = [1, 1, 3, 3, 2, 5]
result = []
ids.count.times do |j|
repeats[j].times { result << ids[j] }
end
This is a one way of doing it:
a = [2,4,5,6,2,3]
b = [1,1,3,3,2,5]
c = []
a.each.with_index do |index, i|
b[index].to_i.times {c << i }
end
p c

Ruby "bucketize" an array

Suppose I have the following array:
a = (1..10).to_a
Is there a single in-built ruby (or rails) function that is capable or splitting the array into exactly N roughly equal parts while maintaining the order?
I'm looking for something like this:
a.bucketize(3)
=> [[1,2,3,4],[5,6,7],[8,9,10]]
a.bucketize(5)
=> [[1,2],[3,4],[5,6],[7,8],[9,10]]
Hint: each_slice doesn't do this.
Also, I know I could write this function myself and open up the Array class or Enumerable module.
Thanks.
I'd do it like this:
ary = (1..10).to_a
ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
ary.each_slice((ary.length.to_f/5).ceil).to_a
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
It's not perfect, but it does come close:
ary = (1..9).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
ary.each_slice((ary.length.to_f/2).ceil).to_a
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]]
ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ary.each_slice((ary.length.to_f/4).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
This kind of task is best tackled using a functional approach. Here's a tail-recursive functional implementation (except for the unavoidable << to accumulate efficiently on arrays):
class Array
def bucketize(n, index = 0, acc = [])
return acc if n <= 0 || size <= index
n0 = ((size - index).to_f / n).ceil
bucketize(n - 1, index + n0, acc << self[index, n0])
end
end
(1..9).to_a.bucketize(3)
#=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(1..10).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
(1..11).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
Here's what I ended up doing:
class Array
def bucketize(n)
return [] if (buckets = n.to_i) <= 0
j = length / buckets.to_f
result = each_with_index.chunk { |_, i| (i / j).floor }.map { |_, v| v.map(&:first) }
result << [] until result.length == buckets
result
end
end
Examples:
a = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a.bucketize(1)
=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
a.bucketize(2)
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
a.bucketize(3)
=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
a.bucketize(4)
=> [[1, 2, 3], [4, 5], [6, 7, 8], [9, 10]]
...
a.bucketize(9)
=> [[1, 2], [3], [4], [5], [6], [7], [8], [9], [10]]
...
a.bucketize(11)
=> [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], []]

Resources