Search by array of values in .select query method - ruby-on-rails

I'm having this type of search:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
Witch returns me an array of needed values:
[33458, 33438]
Next i need to check if record exists with select:
serv_objt_attr.select {|array| array.stya_id == values.collect(&:attr_id).uniq}
This is an example what i'm thinking off.
So how to do it with select, so he would walk through all values witch i'm getting from values.
I know that i could to something like
values.collect(&:attr_id).uniq do |val|
serv_objt_attr.select {|array| array.stya_id == val}
end
But i do not thing that this is a good option.
Ruby 1.8.7
Rails 2.3.4

This is a good case for the set intersection operator:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
values & Set.new(serv_objt_attr.map(&:stya_id)
Here's what the & does:
>> values = [1,2,3]
=> [1, 2, 3]
>> other_array = [1,5,9,3]
=> [1, 5, 9, 3]
>> values & other_array
=> [1, 3]

Related

How to replace missing key/value with zero using slice

I have a hash. I need to extract some key/value pairs, but some desired keys are missing.
How can I replace the missing pairs with "key" => 0.0 when I call attributes.slice on the record and keys as follows:
record = Model.last
record.attributes.slice('k1','k2','k3','k4','k5') # this returns
=> {"k1"=> 343, k3=> 0.0}
If some keys are missing then they won't appear in the result. How can I get the remaining missing keys assigned with 0.0?
Suppose
h = { 'k2'=>2, 'k1'=>1 }
and
all_keys = ['k1', 'k2', 'k3', 'k4']
then
all_keys.map { |k| h.fetch(k,0.0) }
#=> [1, 2, 0.0, 0.0]
See Hash#fetch.
We can take advantage of the fact that a hash can be overwritten with new key/value pairs:
{a: 0, b: 0}.merge(a: 2) # => {:a=>2, :b=>0}
Knowing that, we can do something like this:
desired_keys = [:a, :b]
foo = {a: 1}
desired_keys.zip([0] * desired_keys.size).to_h.merge(foo.slice(*desired_keys))
# => {:a=>1, :b=>0}
desired_keys is a predefined list of the key/value pairs we want, foo is the actual hash the real values are coming from.
[0] * 2 # => [0, 0] creates an array of a given size.
desired_keys.zip([0] * desired_keys.size).to_h creates a temporary hash of the values being used as filler.
merge(foo.slice(*desired_keys)) grabs the pairs we wanted. In this situation, * AKA "splat" explodes the array into its individual elements, so they're passed as separate parameters to slice. Here's what's happening:
def bar(*a)
a
end
bar(%w[a b]) # => [["a", "b"]]
bar(*%w[a b]) # => ["a", "b"]
Notice that the first call is passing in an array, whereas the second passes separate values.
Breaking it down a little to make it a bit more apparent:
desired_keys.zip([0] * desired_keys.size).to_h # => {:a=>0, :b=>0}
.merge(foo.slice(*desired_keys)) # => {:a=>1, :b=>0}
Because we know the record fields we're retrieving, it's easy to create that temporary hash once, in advance, then reuse it every time, resulting in very fast code:
DESIRED_KEYS = [:a, :b]
ZERO_HASH = DESIRED_KEYS.zip([0] * DESIRED_KEYS.size).to_h # => {:a=>0, :b=>0}
foo = {a: 1}
ZERO_HASH.merge(foo.slice(*DESIRED_KEYS))
# => {:a=>1, :b=>0}
All the methods, including * are part of Array or Hash.
How to use 0.0 instead of 0 is left as an exercise for the reader.
I think this should work in your case
Model.slice('k1','k2','k3','k4','k5').transform_values! { |v| v ? v : 0.0 }

Rails group by column and select column

I have a table DinnerItem with columns id, name, project_id, client_id, item_id and item_quantity.
I want to fetch data group_by item_id column and the value should only have the item_quantity column value in the format
{ item_id1 => [ {item_quantity from row1}, {item_quantity from row2}],
item_id2 => [ {item_quantity from row3}, {item_quantity from row4} ]
}
How can I achieve it in one single query?
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
But this has the format
{1=>[#<DinnerItem id: nil, item_id: 1, item_quantity: nil>, #<DinnerItem id: nil, item_id: 1, item_quantity: {"50"=>30, "100"=>10}>], 4=>[#<DinnerItem id: nil, item_id: 4, item_quantity: {"100"=>5, "1000"=>2}>}
Something like this should do the job:
result = OfferServiceModels::DinnerItem
.pluck(:item_id, :item_quantity)
.group_by(&:shift)
.transform_values(&:flatten)
#=> {1 => [10, 20], 2 => [30, 40]}
# ^ item id ^^ ^^ item quantity
A step by step explanation:
# retrieve the item_id and item_quantity for each record
result = OfferServiceModels::DinnerItem.pluck(:item_id, :item_quantity)
#=> [[1, 10] [1, 20], [2, 30], [2, 40]]
# ^ item id ^^ item quantity
# group the records by item id, removing the item id from the array
result = result.group_by(&:shift)
#=> {1 => [[10], [20]], 2 => [[30], [40]]}
# ^ item id ^^ ^^ item quantity
# flatten the groups since we don't want double nested arrays
result = result.transform_values(&:flatten)
#=> {1 => [10, 20], 2 => [30, 40]}
# ^ item id ^^ ^^ item quantity
references:
pluck
group_by
shift
transform_values
flatten
You can keep the query and the grouping, but append as_json to the operation:
DinnerItem.select(:item_id, :item_quantity).group_by(&:item_id).as_json
# {"1"=>[{"id"=>nil, "item_id"=>1, "item_quantity"=>1}, {"id"=>nil, "item_id"=>1, "item_quantity"=>2}],
# "2"=>[{"id"=>nil, "item_id"=>2, "item_quantity"=>1}, {"id"=>nil, "item_id"=>2, "item_quantity"=>2}]}
Notice as_json will add the id of each row which will have a nil value.
I don't know that this is possible without transforming the value returned from the db. If you are able to transform this, the following should work to give you the desired format:
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
.transform_values { |vals| vals.map(&:item_quantity) }
# => {"1"=>[nil,{"50"=>30, "100"=>10}],"4"=>...}
# or
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
.transform_values { |vals| vals.map { |val| val.slice(:item_quantity) }
# => {"1"=>[{:item_quantity=>nil},:item_quantity=>{"50"=>30, "100"=>10}}],"4"=>...}
I'd argue there's nothing wrong with the output you're receiving straight from the db though. The data is there, so output the relevant field when needed: either through a transformation like above or when iterating through the data.
Hope this helps in some way, let me know :)

Identifying ascending and descending order or Range

I want to know how we can identify that descending order Range is given.
I have a test case where range is given in (1..-1) and to_a method gives empty array for same.
(1..5).to_a
=> [1, 2, 3, 4, 5]
(5..1).to_a
=> []
Help me write a condition which can confirm descending order Range is given.
Use Range#size:
(5..1).size.zero?
#⇒ true
(1..5).size.zero?
#⇒ false
Range has Range#first and Range#last methods.
r = (5..1)
r.first > r.last # => true
If you want just to check for a reverse range:
(5..1).to_a.empty? #=> true
(1..5).to_a.empty? #=> false
If you want to have an array as output, you could do
r.to_a.then { |ary| ary.empty? ? (r.end..r.begin).to_a.reverse : ary }
The output for the two cases are:
r = (5..1) #=> [5, 4, 3, 2, 1]
r = (1..5) #=> [1, 2, 3, 4, 5]
(1..5).to_a.present? ? "Ascending" : "Descending"
if value present in array means it Ascending or Descending cause if you give (5..1) range it create blank Array.

Ruby:-- Accept array as input and put it into hash

User must enter the array as input. Hash has to accept the input array elements as values.
Create a Ruby program for this by using loops. If array completed print this statement “All array elements are assigned to keys in the hash”
A = [1, 6, 4, 5]
H = {“k1” => 1
“k2” => 6
“k3” => 4
“k4” => 5}
Another solution can be,
a.each_with_index.inject({}) { |m,(a,i)| m["k#{i+1}"] = a; m }
Update: answering to your question
puts 'Enter number of hash elements'
n = gets.to_i
n.times |i|
hash["k#{i+1}"] = gets.to_i
end
puts 'All array elements are assigned to keys in the hash'
A = [1, 6, 4, 5]
Hash[[*'k1'.."k#{A.length}"].zip(A)]

Get keys of activerecord

I've got an active record to which I want to collect its keys in a seperate array
So if my #item.first has
item.a = 1
item.b = 2
item.c = 3
I want to collect an array like [a => 1, b => 2, c => 3].
Is there a way to do this?
This can be done with attributes:
#item.first.attributes
And to select specific attributes you can filter with select as:
#item.first.attributes.select { |key| ['a', 'b', 'c'].include?(key) }
yes you can do it using as_json read this http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json

Resources