Merge collections of objects

J

John Butler

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
 
D

Dave Bass

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. :)
 
A

Alex Fenton

John said:
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
 
S

Saji N. Hameed

* John Butler said:
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 (e-mail address removed)
KOREA
 
S

shortcutter

John said:
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

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
 
S

Sebastian Hungerecker

Dave said:
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
 
E

Eric I.

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 Ruby 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.
 
R

Ron Fox

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
 
J

John Butler

Ron said:
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
 
R

Robert Klemme

2008/5/29 John Butler said:
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.

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
=> #<struct Company rel_a=[], rel_b=nil>

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=[#<struct Person id=1, name="name 1">,
#<struct Person id=2, name="name 2">, #<struct Person i
d=3, name="name 3">], rel_b=nil>
irb(main):023:0> c2 = Company.new [2,3,4].map {|i| Person.new i, "name #{i}"}
=> #<struct Company rel_a=[#<struct Person id=2, name="name 2">,
#<struct Person id=3, name="name 3">, #<struct Person i
d=4, name="name 4">], rel_b=nil>
irb(main):024:0> c1.merge :rel_a, c2, [:id]
=> []
irb(main):025:0> c1
=> #<struct Company rel_a=[#<struct Person id=1, name="name 1">,
#<struct Person id=2, name="name 2">, #<struct Person i
d=3, name="name 3">, #<struct Person id=4, name="name 4">], rel_b=nil>
irb(main):026:0> c2
=> #<struct Company rel_a=[], rel_b=nil>
irb(main):027:0>

Kind regards

robert
 
J

John Butler

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
 
R

Robert Klemme

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,667
Latest member
DaniloB294

Latest Threads

Top