Merge collections of objects

Hi,

I wonder if there is a quick way to do this.

companyA has 2 employees “a”, “b”

companyB has 3 employees “a”, “b”, “c”

I basically want to merge companyA employess with companyB employees so
when i look at companyA the employees will equal “a”, “b”, “c”

i know i could do something like companyA.employees = companyB.employees
but this would just overwrite companyA employees so if companyA had more
employees than companyB i would lose the employees companyA had that
companyB didnt.

I think companyA.employees << companyB.employees would just mash them
together and give me duplicates.

The only other way i can think off is a nasty double loop that compares
each object 1 by 1 and then starts again going through the process.

It would also be nice if the employees for companyB were removed but i
could do that separately.

JB

Put your employees into a single array.
Use the .uniq! method to remove duplicates.

C:> irb
irb(main):001:0> arr1 = [ “a”, “b”, “c” ]
=> [“a”, “b”, “c”]
irb(main):002:0> arr2 = [ “a”, “b” ]
=> [“a”, “b”]
irb(main):003:0> arr3 = arr1 + arr2
=> [“a”, “b”, “c”, “a”, “b”]
irb(main):004:0> arr3.uniq!
=> [“a”, “b”, “c”]
irb(main):005:0>

Now make this more compact. :slight_smile:

John B. wrote:

Hi,

I wonder if there is a quick way to do this.

companyA has 2 employees “a”, “b”

companyB has 3 employees “a”, “b”, “c”

I basically want to merge companyA employess with companyB employees so
when i look at companyA the employees will equal “a”, “b”, “c”

Try the “|” method of Array

irb(main):001:0> comp_a = %w[a b c]
=> [“a”, “b”, “c”]
irb(main):002:0> comp_b = %w[a b]
=> [“a”, “b”]
irb(main):003:0> comp_a | comp_b
=> [“a”, “b”, “c”]
irb(main):004:0> comp_a & comp_b
=> [“a”, “b”]

a

On 28 Mai, 15:42, Alex F. [email protected] wrote:

when i look at companyA the employees will equal “a”, “b”, “c”
=> [“a”, “b”]

a

For efficient processing of large collections there’s also Set and
Hash.

irb(main):001:0> a=%w{a b c}
=> [“a”, “b”, “c”]
irb(main):002:0> b=%w{b c d}
=> [“b”, “c”, “d”]
irb(main):003:0> a.to_set | b.to_set
=> #<Set: {“a”, “b”, “c”, “d”}>

irb(main):004:0> h=Hash.new 0
=> {}
irb(main):005:0> [a,b].each {|c| c.each {|x| h[x]+=1}}
=> [[“a”, “b”, “c”], [“b”, “c”, “d”]]
irb(main):006:0> h.keys
=> [“a”, “b”, “c”, “d”]
irb(main):007:0>

etc.

Kind regards

robert

Hi,

I wonder if there is a quick way to do this.

companyA has 2 employees “a”, “b”

companyB has 3 employees “a”, “b”, “c”

I basically want to merge companyA employess with companyB employees so
when i look at companyA the employees will equal “a”, “b”, “c”
[snip]

Hi,

Is this what you are looking for?

#--------------------------
ayes = %w( john jani janardhan )
bees = %w( john ami samy jani)

for b in bees
ayes << b if ayes.index(b) == nil
end

puts ayes
#--------------------------

saji

Saji N. Hameed

APEC Climate Center +82 51 668 7470
National Pension Corporation Busan Building 12F
Yeonsan 2-dong, Yeonje-gu, BUSAN 611705 [email protected]
KOREA

Dave B. wrote:

irb(main):001:0> arr1 = [ “a”, “b”, “c” ]
=> [“a”, “b”, “c”]
irb(main):002:0> arr2 = [ “a”, “b” ]
=> [“a”, “b”]
irb(main):003:0> arr3 = arr1 + arr2
=> [“a”, “b”, “c”, “a”, “b”]
irb(main):004:0> arr3.uniq!
=> [“a”, “b”, “c”]

arr3 = arr1 | arr2
=> [“a”, “b”, “c”]

HTH,
Sebastian

On May 28, 8:55 am, John B. [email protected] wrote:

Hi,

I wonder if there is a quick way to do this.

companyA has 2 employees “a”, “b”

companyB has 3 employees “a”, “b”, “c”

I basically want to merge companyA employess with companyB employees so
when i look at companyA the employees will equal “a”, “b”, “c”

Hi John,

It’s interesting that you used the word “collections” in your subject
rather than “arrays”. Because Ruby has options when it comes to
collections, and you can often gain something by choosing the best
collection for the job rather than always resorting to Arrays and
Hashes.

And it’s also worth considering what types of operations you’ll be
performing on these collections and how many members you expect to
deal with. I don’t know how Array#uniq is implemented, but it’s most
typically implemented as an O(n**2) or it creates a supplementary data
structure (e.g., hash) to help it along.

The class Set is in the standard library, and as you might imagine a
set is a collection that does not allow duplicates and that does not
maintain order. So it will automatically handle your “problem” of
duplicates. The question is whether maintaining order is important to
your application or not. And if you’re merging a Set of size n into
one of size m, the merge is likely O(n) with no huge memory
requirement (as typically implemented). So it’s likely more efficient
at this operation.

Here’s some sample code:

require ‘set’

company_a = Set.new [‘alice’, ‘bob’, ‘carla’]
company_b = Set.new [‘david’, ‘bob’, ‘ellen’]

print "The employee(s) common to both companies is ",
(company_a & company_b).to_a.join(', '), “.\n”

new_company = company_a + company_b
puts "If we create a new company with a union, we get " +

#{new_company.inspect}."

company_a.merge(company_b)
puts “If we merge one company into the other, we get "
+
#{company_a.inspect}.”

Eric

====

LearnRuby.com offers Rails & Ruby HANDS-ON public & ON-SITE
workshops.
Ruby Fundamentals Wkshp June 16-18 Ann Arbor, Mich.
Ready for Rails R. Wkshp June 23-24 Ann Arbor, Mich.
Ruby on Rails Wkshp June 25-27 Ann Arbor, Mich.
Ruby Plus Rails Combo Wkshp June 23-27 Ann Arbor, Mich
Please visit http://LearnRuby.com for all the details.

Ron F. wrote:

Unless this is a toy project, it will be important to think about what
is it that makes two employees, one in companyA and one in companyB
‘duplicates’…and what you really want to do in that case as the
record information related to the emplyees may be different between
the companies…all this is interesting business logic… not a ruby
question however.

RF

Yes ive figured that out. Its proving a bit of pain especially with has
many through relationships.

For the above i have a company that has many employees through
company_employees so what i basically want to do is.

companyA has 2 employees “a”, “b”

companyB has 3 employees “a”, “b”, “c”

Copy any employees from companyB that are not in companyA
Delete all reference to employees from companyB

So

companyA will have 3 employees “a”, “b”, “c”

companyB will have no employees

Ive tried various tings as there are a lot of associations i need to
copy over so im trying to create one method that will deal with this
passing through the 2 objects and the relationship in dyn_method:

dyn_method = ‘employees’
myarray = Array.new
myarray = companyA.send(dyn_method) | company_b.send(dyn_method)
companyA.send(dyn_method + ‘=’,myarray)

The below works as in it updates all the company_id links in the
company_employees table with companyA’s id so the employees are only
linked to companyA and not referenced by companyB anymore. The
duplicates causes an issue here though.
dyn_method = ‘company_employees’
companyA.send(dyn_method + ‘=’,companyB.send(dyn_method))

Im still looking for the best solution for adding to a company which has
no employees and then to a company that already has employees but
ignoring the duplicates.

JB

2008/5/29 John B. [email protected]:

Copy any employees from companyB that are not in companyA
passing through the 2 objects and the relationship in dyn_method:
dyn_method = ‘company_employees’
companyA.send(dyn_method + ‘=’,companyB.send(dyn_method))

Im still looking for the best solution for adding to a company which has
no employees and then to a company that already has employees but
ignoring the duplicates.

Here is one way with a slightly different setting: I used a Struct
generated class because that has attribute access similar to a Hash:

Parameters are

  1. the symbol of the relationship
  2. the other instance
  3. an optional list of key fields, if missing the object is the key

irb(main):001:0> Company = Struct.new :rel_a, :rel_b do
irb(main):002:1* def merge(rel, from, keys = nil)
irb(main):003:2> from_r = from[rel]
irb(main):004:2> return if from_r.nil? || from_r.empty?
irb(main):005:2>
irb(main):006:2* tmp = ((self[rel] || []) + from_r).inject({}) do
|h,o|
irb(main):007:3* k = keys ? keys.map {|k| o.send(k)} : o
irb(main):008:3> h[k] ||= o
irb(main):009:3> h
irb(main):010:3> end
irb(main):011:2>
irb(main):012:2* self[rel] = tmp.values
irb(main):013:2> from_r.clear
irb(main):014:2> end
irb(main):015:1> end
=> Company
irb(main):016:0> c1 = Company.new [1,2,3]
=> #<struct Company rel_a=[1, 2, 3], rel_b=nil>
irb(main):017:0> c2 = Company.new [2,3,4]
=> #<struct Company rel_a=[2, 3, 4], rel_b=nil>
irb(main):018:0> c1.merge :rel_a, c2
=> []
irb(main):019:0> c1
=> #<struct Company rel_a=[1, 2, 3, 4], rel_b=nil>
irb(main):020:0> c2
=> #

Now an example with key fields:

irb(main):021:0> Person = Struct.new :id, :name
=> Person
irb(main):022:0> c1 = Company.new [1,2,3].map {|i| Person.new i, “name
#{i}”}
=> #<struct Company rel_a=[#,
#, #], rel_b=nil>
irb(main):023:0> c2 = Company.new [2,3,4].map {|i| Person.new i, “name
#{i}”}
=> #<struct Company rel_a=[#,
#, #], rel_b=nil>
irb(main):024:0> c1.merge :rel_a, c2, [:id]
=> []
irb(main):025:0> c1
=> #<struct Company rel_a=[#,
#, #, #], rel_b=nil>
irb(main):026:0> c2
=> #
irb(main):027:0>

Kind regards

robert

Unless this is a toy project, it will be important to think about what
is it that makes two employees, one in companyA and one in companyB
‘duplicates’…and what you really want to do in that case as the
record information related to the emplyees may be different between
the companies…all this is interesting business logic… not a ruby
question however.

RF

On 29.05.2008 20:03, John B. wrote:

working now but i may change to your solution above. My solution doesnt
feel the most effecient.

for obj in company_b.send(dyn_method)
if !company_a.send(dyn_method).detect { |t| t.id == obj.id}
company_a.send(dyn_method) << obj
end
end

Efficiency large depends on the sizes of collections involved. Your
approach can actually be faster for smaller sets. You have to measure
it.

Kind regards

robert

Robert K. wrote:

2008/5/29 John B. [email protected]:

Copy any employees from companyB that are not in companyA
passing through the 2 objects and the relationship in dyn_method:
dyn_method = ‘company_employees’
companyA.send(dyn_method + ‘=’,companyB.send(dyn_method))

Thats pretty nice, as you say the struct is pretty similar to the hash
so multiple keys can be checked for duplicates. This is how i have it
working now but i may change to your solution above. My solution doesnt
feel the most effecient.

for obj in company_b.send(dyn_method)
if !company_a.send(dyn_method).detect { |t| t.id == obj.id}
company_a.send(dyn_method) << obj
end
end