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

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

Related

How to split array to sub-arrays of elements with same values ? (ruby) [duplicate]

This question already has answers here:
Ruby - collect same numbers from array into array of arrays
(4 answers)
Closed 2 years ago.
I have the array [1, 2, 2, 2, 3, 4, 4, 5, 6]
Is there any elegant way I can split it to sub-arrays, so that each array contains elements with same values?
I want to achieve the following result:
result = [[1], [2, 2, 2], [3], [4, 4], [5], [6]]
Thank you for your help !
Input
a = [1, 2, 2, 2, 3, 4, 4, 5, 6]
Code
p a.slice_when { |a, b| a != b }.to_a
output
[[1], [2, 2, 2], [3], [4, 4], [5], [6]]

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

Given an array A[] and a number x, check for pair in A[] with sum as x

Given an array A[] and a number x, check for pair in A[] with sum as x. can anyone help me out on this one in rails?
The ruby array #combination method can give you all combinations of array members of a given number of elements.
[1, 2, 3, 4, 5, 6].combination(2).to_a
=> [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], ... [5,6]]
Then you just want to select the elements where they add up to a given number.
[1, 2, 3, 4, 5, 6]combination(2).to_a.select{|comb| comb[0] + comb[1] == 7}
=> [[1, 6], [2, 5], [3, 4]]
To make it work for a different number of combined elements (e.g. 3 instead of 2) you can do...
[1, 2, 3, 4, 5, 6]combination(3).to_a.select{|c| (c.inject(0) {|sum,x| sum + x}) == 7}
This will work for 2, 3, 4, or any number up to the full array size.
It works by
finding combinations of 3
using `#inject' to sum all the elements of each combination
comparing that sum to the target number
You can easily achieve it by own function as:
def sum_as_x?(ary,x)
num=a.find{|e| ary.include?(x-e)}
unless num
puts "not exist"
else
p [x-num,num]
end
end
a = [1,2,3,4,5]
sum_to_x?(a,9)
>> [5, 4]
sum_to_x?(a,20)
>> not exist

Is it possible to get the difference from two dynamic arrays regardless of their order?

If I have two arrays and I try to find their difference..
[1, 2, 3, 2, 6, 7] - [2, 1]
I get :
[3, 6, 7]
But if I flip those arrays around
[2, 1] - [1, 2, 3, 2, 6, 7]
I get :
[]
My question is, being that my two arrays are dynamic, I need to know if there is a difference in between both arrays regardless of their order. What's the simplest expression to find that?
You can define it:
class Array
def diff(o)
(o - self) + (self - o) # alternatively: (o + self) - (o & self)
end
end
[2, 1].diff [1, 2, 3, 2, 6, 7] # [3, 6, 7]
[1, 2, 3, 2, 6, 7].diff [2, 1] # [3, 6, 7]
[2, 3, 3, 1].diff [2, 4, 5] # [4, 5, 3, 3, 1]
[2, 4, 5].diff [2, 3, 3, 1] # [3, 3, 1, 4, 5]
The correct answer probably depends on what you want in the end, though, as the second two examples above show.
If you only want unique values, you'll want to convert the two inputs to sets first, and return the result as an array:
class Array
def diff(o)
(o.to_set ^ to_set).to_a # or simply (o.to_set ^ self).to_a
end
end
[2, 4, 5].diff [2, 3, 3, 1] # [4, 5, 3, 1]
(There might be a built-in Rails method, too.)
Using Set#^:
require 'set'
([2, 1].to_set ^ [1, 2, 3, 2, 6, 7]).to_a
# => [3, 6, 7]
([1, 2, 3, 2, 6, 7].to_set ^ [2, 1]).to_a
# => [3, 6, 7]
According to the documentation:
Set#^ returns a new set containing elements exclusive between the set and
the given enumerable object.

ruby array iteration

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

Resources