1

Lets say we have an array containing ids of objects that need to be fetched

This is the ids that need to fetched from another object array

`idvalues=[2,3]`

This is the object array

 array_object=[#<CustomPricing id: 2, base_price: 500>, #<CustomPricing id: 3, base_price: 700>, #<CustomPricing id: 4, base_price: 900>, #<CustomPricing id: 2, base_price: 500>]

How can we fetch objects with the id's 2 & 3 and put them in new array. Here also there are multiple records with same id so just want the first record only.

I tried like this

events = idvalues.each {|id_value| array_object.find(id=id_value)}

but this returned the idvalue itself which is [2,3]. How can we achieve this?

6
  • idvalues.map instead of each Commented Jun 11, 2018 at 18:25
  • @engineersmnky tried that before but that returns all ids with 2 and 3. I just want the first record for each ids. Also if I try array_object.find(id=id_value).first it returns just 2 object with same id Commented Jun 11, 2018 at 18:31
  • sorry you need to change find to find {|cp| cp.id == id_value} not even sure how this is working for you right now if array_object is actually an Array Commented Jun 11, 2018 at 18:33
  • 1
    idvalues.map {|id| array_object.find { |o| o.id == id} } Commented Jun 11, 2018 at 18:37
  • 1
    where are these values coming from as it seems like the database would be the best place to handle this e.g. CustomPrice.distinct.where(id: idvalues) Commented Jun 11, 2018 at 18:57

2 Answers 2

3

One solution would be:

grouped = array_object.group_by(&:id)
selected = idvalues.uniq.map { |id| grouped[id].first }

(the uniq can be removed if the idvalues array does not contain any duplicates)

Or another version

selected = array_object.group_by(&:id).values_at(*id_values).map(&:first)

(see comment by @mudasobwa to this answer)

This first groups the pricings by id and then uses map to find a mapping of ids to a the first pricing with that ID.

But the comment to your question by @mudasobwa shows a easier way to solve this:

selected = idvalues.map {|id| array_object.find { |o| o.id == id} }

Question remaining: why does array_object even contains duplicate pricings?

If they were unique you could just use select:

selected = array_object.select { |pricing| idvalues.includes?(pricing.id) }

You could make the array uniq first:

selected = array_object.select { |pricing| idvalues.includes?(pricing.id) }

(if the objects in the array have a proper equality operator implemented)

But honestly I think the array should not contain duplicate items in the first place. This sounds like a Query that could be optimized?

Sign up to request clarification or add additional context in comments.

14 Comments

array_object.group_by(&:id).values_at(*id_values).map(&:first)
Easier, but slower :) group_by seems to be a proper way to go.
@engineersmnky yes its still returning same ids
I'm sure you'll find a answer that works for you :-)
Abhilash, if n = array_object.size and m = id_values.size, computational efficiency is O(n*m) for the former and O(n+m) for the latter. That is, the problem with the former is that part of array_object must be traversed for each element of id_values. As I explained in my comments, however, the use of group_by does not appear to guarantee the production of a correct solution.
|
1

Here is a way to extract the desired instances from array_object that is intended to be relatively efficient.

Code

require 'set'

def extract(array_object, id_values)
  idv = id_values.to_set
  arr = []
  array_object.each do |obj|
    id = obj[:id]
    if idv.include?(id)
      arr << obj
      idv.delete(id)
    end
    break if idv.empty?
  end 
  idv.empty? ? arr : nil
end

Example

id_values = [2, 3]

CustomPricing = Struct.new(:id, :base_price) {}
array_object = [[3, 700], [2, 500], [4, 900], [2, 500]].map do |id, bp|
  CustomPricing.new(id, bp)
end
  #=> [#<struct CustomPricing id=3, base_price=700>,
  #    #<struct CustomPricing id=2, base_price=500>,
  #    #<struct CustomPricing id=4, base_price=900>,
  #    #<struct CustomPricing id=2, base_price=500>]

Readers unfamiliar with structs (or needing a brush-up) may wish to read this article.

extract(array_object, id_values)
  #=> [#<struct CustomPricing id=3, base_price=700>,
  #    #<struct CustomPricing id=2, base_price=500>]

If the objects are to ordered by the values of :id that reflect their order in id_values, the penultimate line (idv.empty? ? arr : nil) can be replaced by the following.

idv.empty? ? arr.sort_by { |obj| id_values.index(obj[:id]) } : nil

which produces the following return value.

[#<struct CustomPricing id=2, base_price=500>,
 #<struct CustomPricing id=3, base_price=700>]

1 Comment

Thank you will have a look at this. In the accepted answer both selected = idvalues.map {|id| array_object.find { |o| o.id == id} } and selected =array_object.group_by(&:id).values_at(*id_values).map(&:first) works for me. Is this approach more efficient than these?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.