Order array depending on appearance in another - ruby-on-rails

Lets say I have 2 arrays of users.
[user1, user2, user3]
[user3]
Based on the second array, I want to sort the first array so that occurrences in the second array appear first in the first array.
So the result of the first array would be:
[user3, user1, user2]
I realise the simple way would be to iterate over the first array and populate an empty array, ordering it if the second array contains it, then merging the rest. The below is pseudo code and untested, but gives an idea of what I was thinking as a simple solution
return_array = []
array1.each do |a|
if array2.include? a
return_array.push array1.pop(a)
end
end
return_array.merge array1
Is there any way to refine this? Built in rails or ruby methods for example.

You should use array intersection and array difference:
a&b + a-b
would give you what you're looking for.
The manual for intersection: http://ruby-doc.org/core-2.2.0/Array.html#method-i-26
The manual for difference: http://ruby-doc.org/core-2.2.0/Array.html#method-i-2D

You could just do this:
array2 + (array1 - array2)

my_array = ['user1', 'user2', 'user3']
my_array2 = ['user3']
my_array2 + (my_array.sort - my_array2)

Related

Better way to search array in Ruby?

I have an array of hashes:
arr = [{"id"=>"1", "name"=>"Alan"}, {"id"=>"2", "name"=>"Ben"}, {"id"=>"3", "name"=>"Carl"}, {"id"=>"4", "name"=>"Danny"}, {"id"=>"5", "name"=>"Eva"}]
If I were to find the name of id #4:
arr.find{ |a| a["id"] == "4" }["name"]
returns "Danny", which is what I want.
My question is, is there a shorter, more elegant way of accomplish the same search?
If you only need to look up one id, a linear search as you are doing is fine.
If you are doing many lookups on the same array, create a hash from the array, so the individual lookups are O(1)
h = Hash[arr.map{|a|[a["id"], a["name"]]}]
h["4"]
It is a perfectly reasonable way of doing it. The only problem I see is that your data structure is not lookup friendly. You need to search in array, instead of looking up by ID in hash. Searching in array is O(N) and is relatively more difficult code. Looking up in hash is O(1) and is no code at all.
So, I would convert your array to hash, and then look up in it. Especially, if you are planning to do many lookups.
people = arr.inject({}) {|memo,v| memo[v["id"].to_i]=v["name"]; memo}
people[4] # will return Danny
people[3] # will return Carl
Hope it helps!

re-organize array element in my case

Suppose I have an array of string like following:
str_arr=["10$", "10$ I am a student","10$Good","10$ Nice weekend!"]
I would like to re-organize the array element value in the way that, in each element of the array , if there is(are) white space(s) after 10$ sign, then, combine the 10$ with the following word.
That's generating a new array like following:
str_arr=["10$", "10$I am a student","10$Good","10$Nice weekend!"]
What I tried to do is like following:
str_arra.map{|elem|
# not sure how to do here,
#split and check then combine again?
if elem.size>1
words=elem.split()
if words[0]=='10$'
#not sure how to do here
end
elsif elem.size==1
elem
end
}
but not sure how to generate the new array ... and the code above seems verbose...
P.S. it is possible that there are multiple white-spaces after 10$, then comes a word
If you only have those cases, the following should do the trick:
["$ abc", "$str"].map {|v, k| v.sub(/\$ +/, '$')}
Here's an example: http://codepad.org/XHeo7E8B
Do this:
str_arra.map{|elem| elem.gsub(/^\$ /, "$") }

How to create object array in rails?

I need to know how to create object array in rails and how to add elements in to that.
I'm new to ruby on rails and this could be some sort of silly question but I can't find exact answer for that. So can please give some expert ideas about this
All you need is an array:
objArray = []
# or, if you want to be verbose
objArray = Array.new
To push, push or use <<:
objArray.push 17
>>> [17]
objArray << 4
>>> [17, 4]
You can use any object you like, it doesn't have to be of a particular type.
Since everything is an object in Ruby (including numbers and strings) any array you create is an object array that has no limits on the types of objects it can hold. There are no arrays of integers, or arrays of widgets in Ruby. Arrays are just arrays.
my_array = [24, :a_symbol, 'a string', Object.new, [1,2,3]]
As you can see, an array can contain anything, even another array.
Depending on the situation, I like this construct to initialize an array.
# Create a new array of 10 new objects
Array.new(10) { Object.new }
#=> [#<Object:0x007fd2709e9310>, #<Object:0x007fd2709e92e8>, #<Object:0x007fd2709e92c0>, #<Object:0x007fd2709e9298>, #<Object:0x007fd2709e9270>, #<Object:0x007fd2709e9248>, #<Object:0x007fd2709e9220>, #<Object:0x007fd2709e91f8>, #<Object:0x007fd2709e91d0>, #<Object:0x007fd2709e91a8>]
Also if you need to create array of words next construction could be used to avoid usage of quotes:
array = %w[first second third]
or
array = %w(first second third)

get first value in hash within hash

Is there a simple way, instead of looping my entire array to fetch the first value of every inner array.
so in essence I have the following;
array = [['test', 'test2'...], ['test' ....]]
so I want to grab array[#][0] and store the unqiue values.
EDIT
Is there a similar way to use the transpose method for arrays with Hash?
I essentially want to do the same thing
Hash = {1=> {1=> 'test', .....}, 2=> {1=> 'test',....}
so at the end I want to have something like new hash variable and leave my existing hash within hash alone.... = {1 => 'test', 2=> 'test2'}
Not sure if I fully understand the question, but if you have a 2 dimensional array (array in array), and you want to turn that into an array of the first element of the second dimension, you can use the map function
firsts = array.map {|array2| array2.first}
The way map works is that it turns one collection into a second collection by applying a function you provide (the block) to each element.
Maybe this?
array.transpose[0]

How do I collect and combine multiple arrays for calculation?

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.

Resources