[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Merge collections of objects

John Butler

5/28/2008 12:56:00 PM

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
--
Posted via http://www.ruby-....

11 Answers

Dave Bass

5/28/2008 1:30:00 PM

0

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. :-)
--
Posted via http://www.ruby-....

Alex Fenton

5/28/2008 1:43:00 PM

0

John Butler 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

Saji N. Hameed

5/28/2008 1:47:00 PM

0

* John Butler <johnnybutler7@gmail.com> [2008-05-28 21:55:51 +0900]:

> 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 saji@apcc21.net
KOREA

Robert Klemme

5/28/2008 2:02:00 PM

0

On 28 Mai, 15:42, Alex Fenton <af...@deleteme.cam.ac.uk> wrote:
> John Butler 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

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

Sebastian Hungerecker

5/28/2008 5:37:00 PM

0

Dave Bass 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
--
NP: Metallica - The Call Of Ktulu
Jabber: sepp2k@jabber.org
ICQ: 205544826

Eric I.

5/28/2008 7:44:00 PM

0

On May 28, 8:55 am, John Butler <johnnybutl...@gmail.com> 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 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://Lea... for all the details.

Ron Fox

5/29/2008 11:26:00 AM

0

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

John Butler 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"
>
> 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

John Butler

5/29/2008 12:49:00 PM

0

Ron Fox 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





--
Posted via http://www.ruby-....

Robert Klemme

5/29/2008 2:06:00 PM

0

2008/5/29 John Butler <johnnybutler7@gmail.com>:
> 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

--
use.inject do |as, often| as.you_can - without end

John Butler

5/29/2008 6:03:00 PM

0

Robert Klemme wrote:
> 2008/5/29 John Butler <johnnybutler7@gmail.com>:
>> 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
--
Posted via http://www.ruby-....