How do I collect and combine multiple arrays for calculation? - ruby-on-rails

I am collecting the values for a specific column from a named_scope as follows:
a = survey_job.survey_responses.collect(&:base_pay)
This gives me a numeric array for example (1,2,3,4,5). I can then pass this array into various functions I have created to retrieve the mean, median, standard deviation of the number set. This all works fine however I now need to start combining multiple columns of data to carry out the same types of calculation.
I need to collect the details of perhaps three fields as follows:
survey_job.survey_responses.collect(&:base_pay)
survey_job.survey_responses.collect(&:bonus_pay)
survey_job.survey_responses.collect(&:overtime_pay)
This will give me 3 arrays. I then need to combine these into a single array by adding each of the matching values together - i.e. add the first result from each array, the second result from each array and so on so I have an array of the totals.
How do I create a method which will collect all of this data together and how do I call it from the view template?
Really appreciate any help on this one...
Thanks
Simon

s = survey_job.survey_responses
pay = s.collect(&:base_pay).zip(s.collect(&:bonus_pay), s.collect(&:overtime_pay))
pay.map{|i| i.compact.inject(&:+) }
Do that, but with meaningful variable names and I think it will work.
Define a normal method in app/helpers/_helper.rb and it will work in the view
Edit: now it works if they contain nil or are of different sizes (as long as the longest array is the one on which zip is called.

Here's a method that will combine an arbitrary number of arrays by taking the sum at each index. It'll allow each array to be of different length, too.
def combine(*arrays)
# Get the length of the largest array, that'll be the number of iterations needed
maxlen = arrays.map(&:length).max
out = []
maxlen.times do |i|
# Push the sum of all array elements at a given index to the result array
out.push( arrays.map{|a| a[i]}.inject(0) { |memo, value| memo += value.to_i } )
end
out
end
Then, in the controller, you could do
base_pay = survey_job.survey_responses.collect(&:base_pay)
bonus_pay = survey_job.survey_responses.collect(&:bonus_pay)
overtime_pay = survey_job.survey_responses.collect(&:overtime_pay)
#total_pay = combine(base_pay, bonus_pay, overtime_pay)
And then refer to #total_pay as needed in your view.

Related

ruby pattern for generating geometrically expanding objects

Given three arrays of unique ids, where the goal is to create individual identifiers that join each member of the three arrays
array_a = [1,2]
array_b = [43,44,47]
array_c = [3,15]
this implies 2 * 3 * 2 individual identifiers (seperated by underscores for legibility purposes):
1_43_3, 1_43_15, 1_44_3, 1_44_15, 1_47_3, 1_47_15, 2_43_3, 2_43_15, 2_44_3, 2_44_15, 2_47_3, 2_47_15
Is there a ruby method that allows to create such a set, i.e. to multiply arrays of arrays ?
Use product method
Input
array_a = [1,2]
array_b = [43,44,47]
array_c = [3,15]
Program
p array_a.product(array_b,array_c).map{|x|x.join("_")}
Output
["1_43_3", "1_43_15", "1_44_3", "1_44_15", "1_47_3", "1_47_15", "2_43_3", "2_43_15", "2_44_3", "2_44_15", "2_47_3", "2_47_15"]
Not to my knowledge, but it's fairly trivial to implement with a couple of loops:
array_a = [1,2]
array_b = [43,44,47]
array_c = [3,15]
combined = array_a.flat_map do |a|
array_b.flat_map do |b|
array_c.map do |c|
[a, b, c].join("_")
end
end
end
Edit - although the solution using product from #Rajagopalan is very neat.
This is not an answer, just shedding light on the two valid answers provided.
Running a performance test in the following manner:
time = Benchmark.measure {
code_to_test
}
puts time
with four data sets:
the first an array with sizes 10x10x10,
the second an array with sizes 20x20x20, which is almost an order of magnitude greater than the former, then
a third array with sizes 30x30x30. and a final
40x40x40, almost another order of magnitude.
The product method returns for each array
user system total
0.002692 0.000340 0.003032
0.057010 0.003608 0.060618
0.078614 0.010978 0.089592
0.217555 0.015326 0.232881
while the nested flat_map array returns
0.002562 0.000145 0.002707
0.077731 0.001857 0.079588
0.085422 0.001829 0.087251
0.263692 0.005506 0.269198
rather indistinguisahble, even at relatively high numbers.

Active Record Array array query - to check records that are present in an array

I have an Objective model which has an attribute called as labels whose values are array data type. I need to query all the Objectives whose labels attribute has values that are present in some particular array.
For Example:
I have an array
a = ["textile", "blazer"]
the Objective.labels may have values as ["textile, "ramen"]
I need to return all objectives that might have either "textile" or "blazer" as one of their labels array values
I tried the following:
Objective.where("labels #> ARRAY[?]::varchar[]", ["textile"])
This returns some records.Now when I try
Objective.where("labels #> ARRAY[?]::varchar[]", ["textile", "Blazer"])
I expect it to return all Objectives which contains at-least one of the labels array value as textile or blazer.
However, it returns an empty array. Any Solutions?
Try && overlap operator.
overlap (have elements in common)
Objective.where("labels && ARRAY[?]::varchar[]", ["textile", "Blazer"])
If you have many rows, a GIN index can speed it up.

In Rails, how do I perform an intersect of two arrays based on a field in each object in the arrays?

I’m using Rails 4.2.7. I have two arrays, (arr1 and arr2) that both contain my model objects. Is there a way to do an intersection on both arrays if an object from arr1 has a field, “myfield1,” (which is a number) that matches an object in arr2? Both arrays will have unique sets of objects. Currently I have
arr1.each_with_index do |my_object, index|
arr2.each_with_index do |my_object2, index|
if my_object.myfield1 == my_object2.myfield1
results.push(my_object)
end
end
end
but this strikes me as somewhat inefficient. I figure there’s a simpler way to get the results I need but am not versed enough in Ruby to know how to do it.
You can build an intersection of the values to find the common values, then select records that have the common values.
field_in_both = arr1.map(&:myfield1) & arr2.map(&:myfield1)
intersection = arr1.select{|obj| field_in_both.include? obj.myfield1} +
arr2.select{|obj| field_in_both.include? obj.myfield1}
I notice in your code, you're only storing records from arr1... if that's correct behaviour then you can simplify my answer
field_in_both = arr1.map(&:myfield1) & arr2.map(&:myfield1)
intersection = arr1.select{|obj| field_in_both.include? obj.myfield1}

How to extract records at position x from an array?

In Rails (or Ruby), is it possible to target and manipulate items at a certain position within an array.
For example, say I have defined an array that cannot exceed 10 records.
#array = Model.where(:my_query = something).order(:my_order).first(10)
Now I want to do something with the first 5 records, and something else with the last five. I could use
#array.first(5)
#array.last(5)
but this falls apart if :my_query returns less than 10 records—i.e. there will be overlap.
#array.at(1)
returns a single position, but what if I need a range of positions. I'm looking for something like
#array.position(1..5)
#array.position(6..10)
Does something like this exist? I'm not sure what search terms I should be Googling?
You want something like this:
first = #array[0..4]
last = #array[5..9]
That will return the first five and the last five elements from the array in two separate variables. If you do it this way and you will not get any overlap.

How to work with tables in Corona SDK

I have a sort of general question but i think that if I tried to be too specific I would only make it very confusing. So basically what I want to know is this:
When you create a table in Corona/Lua you can put pretty much an unlimited number of things in it correct?
So say i create a table called
rectangles = {};
and then i put a bunch of instances of rectangles in it. If i wanted to change a property of ALL the rectangles at once, how could I do it?
I understand how it would work with a set number of items in the table, like:
for i = 1, 10 do
rectangles[i] = display.newImage("rectangle.png");
then to change all of the images x positions for instance you would simply say
rectangles[i].x = 20;
but how would you change a property of all items in the array without knowing how many there are, as in you didnt give an upper bound, and cant because the table is always growing?
For arrays that have only one kind of elements you can use #rectangles for element count.
for i = 1, #rectangles do
rectangles[i] = display.newImage("rectangle.png");
end
Regarding the youtube example,
if you add element into rectangles like this:
rectangles[b]=b;
what it actually does is
rectangles["083DF6B0"]=b"
you see when a display object b is used as a key it is converted into a hex string.
in addition, you would need to use pairs to go over each element as they are
keys (e.g. array.length,array.width,array.weight..) rather than index (e.g. array[2],array[3]..)
for key,value in pairs(rectangles) do
print(key); --prints 083DF6B0
print(value); --prints 20
rectangles[key]=30;
end
It depends on how you're storing items in the table. If you're storing by index (as in your example), you can use ipairs to iterate over indexes and values:
for index,value in ipairs(rectangles) do
value.x = 20
--or
rectangles[index].x = 20
end
If you're storing by key (as in the youtube video you mention in a comment), iterate using pairs:
for key,value in pairs(rectangles) do
value.x = 20
--or
rectangles[key].x = 20
end
Just don't store items using both index and keys, unless you know what to expect.

Resources