ruby array iteration - ruby-on-rails

I have four arrays(named array1,2,3,4) each with 8 objects and want to sort them into 8 empty arrays that will consist of 4 objects each, i.e place each object from array 1 into an empty array.
?? << array1.shift until array.empty?
I'm not sure how to iterate over the 8 empty arrays so that each of them recieves an object from array1
e.g
array1 = clubs1-8
array2 = spades1-8
array3 = hearts1-8
array4 = diamonds1-8
8 empty arrays or players, each player is dealt a card from array1,then 1 card from array2 etc.
Thanks for the answers but I wanted to add the objects iteratively so I could add conditions based on what objects had already been added to each array
e.g
distribute array1 among 8 empty arrays
distribute array2 among the 8 arrays but check before that no array contains the same card number (it can't have both the 2 of hearts and the 2 of diamonds)

Did you mean something like this?
require "matrix"
a1 = (0..7).to_a
a2 = (8..15).to_a
a3 = (16..23).to_a
a4 = (24..31).to_a
Matrix[a1, a2, a3, a4].transpose.to_a #=> [[0, 8, 16, 24], [1, 9, 17, 25], [2, 10, 18, 26], [3, 11, 19, 27], [4, 12, 20, 28], [5, 13, 21, 29], [6, 14, 22, 30], [7, 15, 23, 31]]
Added:
In fact it is even more trivial:
a1.zip(a2, a3, a4) #=> [[0, 8, 16, 24], [1, 9, 17, 25], [2, 10, 18, 26], [3, 11, 19, 27], [4, 12, 20, 28], [5, 13, 21, 29], [6, 14, 22, 30], [7, 15, 23, 31]]

Iteration is not the only way. Try this (assuming a1..a8 are your 8 empty arrays):
a1, a2, a3, a4, a5, a6, a7, a8 = array1.zip(array2, array3, array4)

irb(main):001:0> ar1 = [1,2,3,4,5,6,7,8]
=> [1, 2, 3, 4, 5, 6, 7, 8]
irb(main):002:0> ar2 = [2,3,4,1,2,3,2,1]
=> [2, 3, 4, 1, 2, 3, 2, 1]
irb(main):003:0> ar3 = [4,3,5,6,3,3,4,5]
=> [4, 3, 5, 6, 3, 3, 4, 5]
irb(main):004:0> ar4 = [5,2,5,6,7,2,2,5]
=> [5, 2, 5, 6, 7, 2, 2, 5]
irb(main):005:0> ar1.zip(ar2,ar3,ar4)
=> [[1, 2, 4, 5], [2, 3, 3, 2], [3, 4, 5, 5], [4, 1, 6, 6], [5, 2, 3, 7], [6, 3, 3, 2], [7, 2, 4, 2], [8, 1, 5, 5]]

Related

Can I use cvxpy to split integer-2D-array to two arrays?

I have a problem that I wonder if I can solve using cvxpy:
The problem:
I have a two dimensional integers array and I want to split it to two array in a way that each row of the source array is either in the 1st or 2nd array.
The requirement from these arrays us that for each column, the sum of integers in array #1 will be as close as possible to twice the sum of integers in array #2.
Example:
Consider the input array:
[
[1, 2, 3, 4],
[4, 6, 2, 5],
[3, 9, 1, 2],
[8, 1, 0, 9],
[8, 4, 0, 5],
[9, 8, 0, 4]
]
The sums of its columns is [33, 30, 6, 29] so ideally we are looking for 2 arrays that the sums of their columns will be:
Array #1: [22, 20, 4, 19]
Array #2: [11, 10, 2, 10]
Off course this is not always possible but I looking for the best solution for this problem.
A possible solution for this specific example might be:
Array #1:
[
[1, 2, 3, 4],
[4, 6, 2, 5],
[8, 4, 0, 5],
[9, 8, 0, 4]
]
With column sums: [22, 20, 5, 18]
Array #2:
[
[3, 9, 1, 2],
[8, 1, 0, 9],
]
With column sums: [11, 10, 1, 11]
Any suggestions?
You can use a boolean vector variable to select rows. The only thing left to decide is how much to penalize errors. In this case I just used the norm of the difference vector.
import cvxpy as cp
import numpy as np
data = np.array([
[1, 2, 3, 4],
[4, 6, 2, 5],
[3, 9, 1, 2],
[8, 1, 0, 9],
[8, 4, 0, 5],
[9, 8, 0, 4]
])
x = cp.Variable(data.shape[0], boolean=True)
prob = cp.Problem(cp.Minimize(cp.norm((x - 2 * (1 - x)) * data)))
prob.solve()
A = np.round(x.value) # data
B = np.round(1 - x.value) # data
A and B are the sum of rows.
(array([21., 20., 4., 19.]), array([12., 10., 2., 10.]))

Ruby array to subarray with fall factor

Is there any inbuilt method available to split large array into smaller chunks with dynamic fall factor?
Eg: i=0
src_arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
if batch_size = 5 and fall_factor = 1, first chunk should be [1, 2, 3, 4, 5] and subsequent array chunks should start from start_index = i * (batch_size - fall_factor). ie, start_index will be 0, 4, 8, 12, and
result: [1, 2, 3, 4, 5]
[5, 6, 7, 8, 9]
[9, 10, 11, 12, 13]
[13, 14]
if fall_factor = 2 result should be as below
[1, 2, 3, 4, 5]
[4, 5, 6, 7, 8]
[7, 8, 9, 10, 11]
[10, 11, 12, 13, 14]
I know how to SOLVE this. My question is if any inbuilt method available like each_slice to get this done instead of reinventing.
For example you can use just #step method of Numeric
0.step(src_arr.size - fall_factor - 1, batch_size - fall_factor).map do |ind|
src_arr[ind, batch_size]
end
# fall_factor = 1
# => [[1, 2, 3, 4, 5], [5, 6, 7, 8, 9], [9, 10, 11, 12, 13], [13, 14]]
# fall_factor = 2
# => [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
Code
def doit(arr, batch_size, fall_factor)
arr[batch_size..-1].
each_slice(batch_size-fall_factor).
each_with_object([arr[0,batch_size]]) { |b,c| c << [*c.last[-fall_factor..-1], *b] }
end
Examples
arr = (1..14).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
doit(arr, 5, 1)
#=> [[1, 2, 3, 4, 5], [5, 6, 7, 8, 9], [9, 10, 11, 12, 13], [13, 14]]
doit(arr, 5, 2)
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
doit(arr, 5, 3)
#=> [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7], [5, 6, 7, 8, 9], [7, 8, 9, 10, 11],
# [9, 10, 11, 12, 13], [11, 12, 13, 14]]
doit(arr, 5, 4)
#=> [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8],
# [5, 6, 7, 8, 9], [6, 7, 8, 9, 10], [7, 8, 9, 10, 11], [8, 9, 10, 11, 12],
# [9, 10, 11, 12, 13], [10, 11, 12, 13, 14]]
Explanation
For arr above and:
batch_size = 5
fall_factor = 2
we have:
a = arr[batch_size..-1]
#=> arr[5..-1]
#=> [6, 7, 8, 9, 10, 11, 12, 13, 14]
b = a.each_slice(batch_size-fall_factor)
#=> a.each_slice(3)
#=> #<Enumerator: [6, 7, 8, 9, 10, 11, 12, 13, 14]:each_slice(3)>
We can see the elements of the enumerator b by converting it to an array:
b.to_a
#=> [[6, 7, 8], [9, 10, 11], [12, 13, 14]]
Continuing:
d = [arr[0,batch_size]]
#=> [[1, 2, 3, 4, 5]]
b.each_with_object(d) { |b,c| c << [*c.last[-fall_factor..-1], *b] }
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
To see how the last calculation is performed, let:
e = b.each_with_object(d)
#=> #<Enumerator: #<Enumerator: [6, 7, 8, 9, 10, 11, 12, 13, 14]:
# each_slice(3)>:each_with_object([[1, 2, 3, 4, 5]])>
e.to_a
#=> [[[6, 7, 8], [[1, 2, 3, 4, 5]]],
# [[9, 10, 11], [[1, 2, 3, 4, 5]]],
# [[12, 13, 14], [[1, 2, 3, 4, 5]]]]
We can use Enumerator#next to obtain each element of e that is passed to the block, set the block variables to each of those values and perform the block calculation. The first element is passed to the block:
b, c = e.next
#=> [[6, 7, 8], [[1, 2, 3, 4, 5]]]
b #=> [6, 7, 8]
c #=> [[1, 2, 3, 4, 5]]
The block calculation is therefore:
c << [*c.last[-fall_factor..-1], *b]
#=> c << [*[[1, 2, 3, 4, 5]].last[-2..-1], *[6, 7, 8]]
# c << [*[1, 2, 3, 4, 5][-2..-1], *[6, 7, 8]]
# c << [*[4, 5], *[6, 7, 8]]
# c << [4, 5, 6, 7, 8]
c #=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]
The next element of e is now passed to the block:
b, c = e.next
#=> [[9, 10, 11], [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]]
b #=> [9, 10, 11]
c #=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]
The remaining calculations are performed similarly.
Based on logic shared by you, below is one possible implementation:
b = 5 # batch size
f = 2 # fall factor
indices = (0...src_arr.size).collect {|i| i * (b-f)}.reject {|i| i + f >= src_arr.size}
result = indices.each_with_object([]) do |i, obj|
obj << src_arr[i, b]
end
p result
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]

Build and order an array with the first element of other arrays

I'm trying to transform an array of 3 arrays in an array of 4 where each array is built and ordered with the first element of each other.
I have this:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
And I would like the following:
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
Any ideas?
Use Array#transpose.
your_array.transpose
will do it.
array = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
array.transpose
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
Is this what you were looking for?

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