0

I have a hash, created by .group_by method, with strings as keys and AR objects as values. And I want to get rid of AR objects with duplicated field(other fields might or might not be the same).

To clarify the question I've created this snippet:

require 'ostruct'   

def os_new(fid); OpenStruct.new(fid: fid, rand: rand(100)); end                                                                                                                                              
a = [os_new(1), os_new(2), os_new(3)]                                                                                                                                                
b = [os_new(3), os_new(3), os_new(2)]                                                                                                                                                
c = [os_new(5), os_new(5), os_new(5), os_new(5)] 

grouped_hash = { a: a, b: b, c: c} 

grouped_hash here is:

# {:a=>[#<OpenStruct fid=1, rand=7>, #<OpenStruct fid=2, rand=50>, #<OpenStruct fid=3, rand=13>], 
#  :b=>[#<OpenStruct fid=3, rand=51>, #<OpenStruct fid=3, rand=58>, #<OpenStruct fid=2, rand=88>], 
#  :c=>[#<OpenStruct fid=5, rand=40>, #<OpenStruct fid=5, rand=8>, #<OpenStruct fid=5, rand=45>, #<OpenStruct fid=5, rand=72>]} 

Now, in result I want a hash with unique fids for each key:

# {:a=>[#<OpenStruct fid=1, rand=7>, #<OpenStruct fid=2, rand=50>, #<OpenStruct fid=3, rand=13>], 
#  :b=>[#<OpenStruct fid=3, rand=51>, #<OpenStruct fid=2, rand=88>], 
#  :c=>[#<OpenStruct fid=5, rand=40>]} 

My solution requires several actions and looks ugly.

grouped_hash.each_value do |value|                                                                                                                                                     
  fids = []
  value.delete_if do |object|
    if fids.include? object.fid                                                                                                                                                            
      true
    else                                                                                                                                                                                   
      fids << object.fid
      false
    end                                                                                                                                                                                
  end
end

I am looking for the most elegant(possibly 1-liner) and efficient way(without hash re-creation).

Rustam Gasanov
  • 15,290
  • 8
  • 59
  • 72

3 Answers3

2

grouped_hash.each_value(&:uniq)

According to AR objects, Rails deliberately delegates equality checks to the identity column. If you want to know two AR objects contain the same values then compare the result of calling #attributes on both.

Tensho
  • 763
  • 1
  • 7
  • 23
1

Not exactly one-liner (although you can write it in one line too):

grouped_hash.inject({}) do |hash, element|
  hash[element.first] = element.last.uniq(&:fid)
  hash
end
katafrakt
  • 2,438
  • 15
  • 19
  • I've updated a question in order to not get confused by `id` and to demonstrate that there are other fields in each object, so `uniq` won't work – Rustam Gasanov Jan 09 '15 at 10:36
  • Thank you! I got the idea! You solution re-creates a hash, so I combined it with @Tensho answer and got what I wanted – Rustam Gasanov Jan 09 '15 at 16:36
0

By combining Tensho and katafrakt (thank you guys) answers I made this:

grouped_hash.each_value { |v| v.uniq!(&:fid) }
Community
  • 1
  • 1
Rustam Gasanov
  • 15,290
  • 8
  • 59
  • 72