How to remove dups from 2 lists?

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
    list1.each_index do | i |
        list2.each_index do | j |
            if list1[i] == list2[j]
                list1.delete_at ( i )
                list2.delete_at ( j )
            end
        end
    end
    return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
   list1.each_index do | i |
       list2.each_index do | j |
           if list1[i] == list2[j]
               list1.delete_at ( i )
               list2.delete_at ( j )
           end
       end
   end
   return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

Having some examples might help, but is something like this what you're after?

>> a = %w[ a a a b b c ]
=> ["a", "a", "a", "b", "b", "c"]
>> b = %w[ b c c c d ]
=> ["b", "c", "c", "c", "d"]
>> a + b
=> ["a", "a", "a", "b", "b", "c", "b", "c", "c", "c", "d"]
>> a | b
=> ["a", "b", "c", "d"]
>> a - b
=> ["a", "a", "a"]
>> a & b
=> ["b", "c"]
>> a - (a & b)
=> ["a", "a", "a"]
>> b - (a & b)
=> ["d"]

In particular, which do you expect from RemoveDupsFromLists( a, b )?

What I have for the last two expressions above:
=> [ ["a", "a", "a"], ["d"] ]

Or:
=> [ ["a", "a", "a", "b"], ["c", "c", "d"] ]
because it works like canceling terms in a fraction:

  ["a", "a", "a", "b", "b", "c"]

···

On May 10, 2007, at 10:18 PM, Mike Steiner wrote:
-----------------------------------------------
                      ["b", "c", "c", "c", "d"]

Basically, write a test:

require 'test/unit'
class RemoveDupsTest < Test::Unit::TestCase
   def test_simple
     list1 = ["a", "a", "a", "b", "b", "c"]
     list2 = ["b", "c", "c", "c", "d"]
     expects = [ [__,__], [__,__] ] #<=== fill me it!
     assert_equals expects, RemoveDupsFromLists(list1, list2)
   end
end

Then you'll know what you want (and so will we!) and you'll be sure when you get it working.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

If I understand correctly, I think this does what you want.

arr1 = %w[a a a b b b c d d e e]
arr2 = %w[a b b b c c c d d d d d e a b]

str1 = arr1.to_s
str2 = arr2.to_s

(arr1 & arr2).each do |x|
str1.sub!(x,"")
str2.sub!(x,"")
end

p str1.split(//).to_a
p str2.split(//).to_a

Harry

···

On 5/11/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have

--

A Look into Japanese Ruby List in English

Here is how I would write it: (it didn't give quite the same results as your method but seems to be closer to what you asked for so...):

def remove_duplicates(list1,list2)
   (list1 & list2).each do |x|
     list1.delete(x)
     list2.delete(x)
   end
   return list1,list2
end

irb(main):028:0> RemoveDupsFromLists([1,1,2,3,4],[2,2,3,5,5])
=> [[1, 1, 4], [2, 5, 5]]
irb(main):029:0> remove_duplicates([1,1,2,3,4],[2,2,3,5,5])
=> [[1, 1, 4], [5, 5]]

Be careful with your method since it alters the Arrays as you iterate through them and will give hard to understand results sometimes. In the example above '2' is a duplicate but is only deleted from the second array once because of this problem/feature.

Alex Gutteridge

Bioinformatics Center
Kyoto University

···

On 11 May 2007, at 11:18, Mike Steiner wrote:

def RemoveDupsFromLists ( list1 , list2 )
   list1.each_index do | i |
       list2.each_index do | j |
           if list1[i] == list2[j]
               list1.delete_at ( i )
               list2.delete_at ( j )
           end
       end
   end
   return [ list1 , list2 ]
end

I'm also not real sure of what exactly you wanted but..
I assumed the following :

def remove_dups_from_both_lists (list1, list2)
  list1_dup = list1.dup
  remove_dups_from_first_list list1, list2
  remove_dups_from_first_list list2, list1_dup
end

def remove_dups_from_first_list(list_to_prune, list_with_dups)
  hash_with_occurrence_count = get_hash_with_occurrence_count list_to_prune
  decrement_count_for_dups(list_with_dups, hash_with_occurrence_count)
  get_remaining_item_list(list_to_prune, hash_with_occurrence_count)
end

def get_hash_with_occurrence_count(list)
  hsh = Hash.new { |h,k| h[k] = 0}
  list.each { |item| hsh[item] += 1 }
  hsh
end

def decrement_count_for_dups(list, other_list_as_hash)
  list.each { |item| other_list_as_hash[item] -= 1 }
end

def get_remaining_item_list(list, list_as_hash)
  list.each_with_index do |item, idx|
    if (list_as_hash[item] > 0)
      list_as_hash[item] -= 1
    else
      list[idx] = nil
    end
  end
  list.compact
end

list1 = %w{one one two three four four five}
list2 = %w{one three three four five five five}
puts "before --- list1:#{list1}"
puts "before --- list2:#{list2}"
remove_dups_from_both_lists list1, list2
puts "after --- list1:#{list1}"
puts "after --- list2:#{list2}"

···

On 5/10/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
    list1.each_index do | i |
        list2.each_index do | j |
            if list1[i] == list2[j]
                list1.delete_at ( i )
                list2.delete_at ( j )
            end
        end
    end
    return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

This is how I would do it (that is, if I understand the OP's request).

a = %w( a a b b c e f)
b = %w( b c c c d e e g)

ha = {}; hb = {}; dif = {}
ha.default = hb.default = 0
ca = []; cb = []

a.uniq.each { |i| a.each { |j| ha[i] += 1 if i == j } }
b.uniq.each { |i| b.each { |j| hb[j| += 1 if i == j } }
(ha.merge hb).keys.each { |k| dif[k] = ha[k] - hb[k] }
dif.each { |k,v| v>0 ? ca += (k*v).split(//) : cb += (k*-v).split(//) }

puts "list1: #{ (dif.each { |k,v| (k*v).split(//) if v>=0 }" # ["a", "a",
"b", "f"]
puts "list2: #{ (dif.each { |k,v| (k*-v).split(//) if v<0 }" # ["c", "c",
"d", "e", "g"]

Of course, you can only compare 2 lists.

Todd

After thinking about your question again, I think you meant something
a little different than what I was thinking before, I think :slight_smile: :slight_smile:
This is no shorter than your code, just different.

arr1 = %w[a b car car b c r c car c c r d]
arr2 = %w[a r1 a b c c car d r r r d]

counts1 = Hash.new(0)
arr1.each {|x| counts1 += 1}

counts2 = Hash.new(0)
arr2.each {|x| counts2 += 1}

new1 =
new2 =

arr1.uniq.each do |x|
(counts1 - counts2).times {new1 << x} if counts1 > counts2
end

arr2.uniq.each do |x|
(counts2 - counts1).times {new2 << x} if counts2 > counts1
end

p new1 #["b","car","car", "c", "c"]
p new2 #["a","r1","d","r"]

Harry

···

On 5/11/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
    list1.each_index do | i |
        list2.each_index do | j |
            if list1[i] == list2[j]
                list1.delete_at ( i )
                list2.delete_at ( j )
            end
        end
    end
    return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

--

A Look into Japanese Ruby List in English

Oops! Correction.

arr1 = %w[a a a b b b c d d e e]
arr2 = %w[a b b b c c c d d d d d e a b]

str1 = arr1.to_s
str2 = arr2.to_s

(arr1 & arr2).each do |x|
str1.sub!(x,"")
str2.sub!(x,"")
end

p str1.split(//)
p str2.split(//)

···

On 5/11/07, Harry Kakueki <list.push@gmail.com> wrote:

On 5/11/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:
> I'm trying to write some code that removes all elements from 2 lists that
> are in both lists. However, I don't want any duplicates from each list
> deleted also (which is what the array "-" operator does). The code I have

If I understand correctly, I think this does what you want.

--

A Look into Japanese Ruby List in English

Sets also come in handy - especially if those lists are large.

Kind regards

  robert

···

On 11.05.2007 06:46, Alex Gutteridge wrote:

On 11 May 2007, at 11:18, Mike Steiner wrote:

def RemoveDupsFromLists ( list1 , list2 )
   list1.each_index do | i |
       list2.each_index do | j |
           if list1[i] == list2[j]
               list1.delete_at ( i )
               list2.delete_at ( j )
           end
       end
   end
   return [ list1 , list2 ]
end

Here is how I would write it: (it didn't give quite the same results as your method but seems to be closer to what you asked for so...):

def remove_duplicates(list1,list2)
  (list1 & list2).each do |x|
    list1.delete(x)
    list2.delete(x)
  end
  return list1,list2
end

irb(main):028:0> RemoveDupsFromLists([1,1,2,3,4],[2,2,3,5,5])
=> [[1, 1, 4], [2, 5, 5]]
irb(main):029:0> remove_duplicates([1,1,2,3,4],[2,2,3,5,5])
=> [[1, 1, 4], [5, 5]]

Be careful with your method since it alters the Arrays as you iterate through them and will give hard to understand results sometimes. In the example above '2' is a duplicate but is only deleted from the second array once because of this problem/feature.

Sorry, the puts lines should read:

puts "list1: #{ca.inspect}"
puts "list2: #{cb.inspect}"

···

On 5/11/07, Todd Benson <caduceass@gmail.com> wrote:

This is how I would do it (that is, if I understand the OP's request).

a = %w( a a b b c e f)
b = %w( b c c c d e e g)

ha = {}; hb = {}; dif = {}
ha.default = hb.default = 0
ca = ; cb =

a.uniq.each { |i| a.each { |j| ha[i] += 1 if i == j } }
b.uniq.each { |i| b.each { |j| hb[j| += 1 if i == j } }
(ha.merge hb).keys.each { |k| dif[k] = ha[k] - hb[k] }
dif.each { |k,v| v>0 ? ca += (k*v).split(//) : cb += (k*-v).split(//) }

puts "list1: #{ (dif.each { |k,v| (k*v).split(//) if v>=0 }" # ["a", "a",
"b", "f"]
puts "list2: #{ (dif.each { |k,v| (k*-v).split(//) if v<0 }" # ["c", "c",
"d", "e", "g"]

Of course, you can only compare 2 lists.

Todd

I'm looking for a function that will eliminate all matching items, like
canceling terms in a fraction. If the first list has 3 "a"s, and the second
list has 5 "a"s, then afterwards the first list should have 0 "a"s and the
second list should have 2 "a"s.

Mike Steiner

···

On 5/10/07, Rob Biedenharn <Rob@agileconsultingllc.com> wrote:

On May 10, 2007, at 10:18 PM, Mike Steiner wrote:

> I'm trying to write some code that removes all elements from 2
> lists that
> are in both lists. However, I don't want any duplicates from each list
> deleted also (which is what the array "-" operator does). The code
> I have
> now doesn't handle restarting the current iteration for both loops
> when a
> match is found and deleted in both loops. Here's the code:
>
> def RemoveDupsFromLists ( list1 , list2 )
> list1.each_index do | i |
> list2.each_index do | j |
> if list1[i] == list2[j]
> list1.delete_at ( i )
> list2.delete_at ( j )
> end
> end
> end
> return [ list1 , list2 ]
> end
>
> What's weird is that doing this is easy in C (my first language), but
> difficult in Ruby. Everything else I've seen has been MUCH easier
> in Ruby.
>
> Mike Steiner

Having some examples might help, but is something like this what
you're after?

>> a = %w[ a a a b b c ]
=> ["a", "a", "a", "b", "b", "c"]
>> b = %w[ b c c c d ]
=> ["b", "c", "c", "c", "d"]
>> a + b
=> ["a", "a", "a", "b", "b", "c", "b", "c", "c", "c", "d"]
>> a | b
=> ["a", "b", "c", "d"]
>> a - b
=> ["a", "a", "a"]
>> a & b
=> ["b", "c"]
>> a - (a & b)
=> ["a", "a", "a"]
>> b - (a & b)
=> ["d"]

In particular, which do you expect from RemoveDupsFromLists( a, b )?

What I have for the last two expressions above:
=> [ ["a", "a", "a"], ["d"] ]

Or:
=> [ ["a", "a", "a", "b"], ["c", "c", "d"] ]
because it works like canceling terms in a fraction:

  ["a", "a", "a", "b", "b", "c"]
-----------------------------------------------
                      ["b", "c", "c", "c", "d"]

Basically, write a test:

require 'test/unit'
class RemoveDupsTest < Test::Unit::TestCase
   def test_simple
     list1 = ["a", "a", "a", "b", "b", "c"]
     list2 = ["b", "c", "c", "c", "d"]
     expects = [ [__,__], [__,__] ] #<=== fill me it!
     assert_equals expects, RemoveDupsFromLists(list1, list2)
   end
end

Then you'll know what you want (and so will we!) and you'll be sure
when you get it working.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

Isn't this what you want?

irb(main):001:0> a = %w{a b c d e f g a b c}
=> ["a", "b", "c", "d", "e", "f", "g", "a", "b", "c"]
irb(main):002:0> b = %w{e f g h i j k h i j}
=> ["e", "f", "g", "h", "i", "j", "k", "h", "i", "j"]
irb(main):003:0> a - (a & b)
=> ["a", "b", "c", "d", "a", "b", "c"]
irb(main):004:0> b - (a & b)
=> ["h", "i", "j", "k", "h", "i", "j"]

Wolfgang Nádasi-Donner

···

On May 10, 2007, at 10:18 PM, Mike Steiner wrote:

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
   list1.each_index do | i |
       list2.each_index do | j |
           if list1[i] == list2[j]
               list1.delete_at ( i )
               list2.delete_at ( j )
           end
       end
   end
   return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

Or is it something like this?

Here only common elements of both lists are deleted from left to right, but if a list contains more elements, e.g. "a" than the other, only the number of "a"s that are in the other list will be deleted from the one, which has more "a"s (I don't know how to describe it - the code should explain ist):

a = %w{a b a a c a b c d e f e a}
b = %w{a b a c d c d c c g h g c}
puts '--- before ---'
p a
p b
ah = a.inject(Hash.new(0)){|h,w|h[w]+=1;h}
bh = b.inject(Hash.new(0)){|h,w|h[w]+=1;h}
abh = (ah.keys & bh.keys).inject({}){|h,w|h[w]=[ah[w], bh[w]].min;h}
[a, b].each{|arr|abh.each{|k,v|v.times{arr.delete_at(arr.index(k)) if arr.include?(k)}}}
puts '--- after ---'
p a
p b

Output:

--- before ---
["a", "b", "a", "a", "c", "a", "b", "c", "d", "e", "f", "e", "a"]
["a", "b", "a", "c", "d", "c", "d", "c", "c", "g", "h", "g", "c"]
--- after ---
["a", "a", "b", "e", "f", "e", "a"]
["d", "c", "c", "g", "h", "g", "c"]

Wolfgang Nádasi-Donner

···

On May 10, 2007, at 10:18 PM, Mike Steiner wrote:

I'm trying to write some code that removes all elements from 2 lists that
are in both lists. However, I don't want any duplicates from each list
deleted also (which is what the array "-" operator does). The code I have
now doesn't handle restarting the current iteration for both loops when a
match is found and deleted in both loops. Here's the code:

def RemoveDupsFromLists ( list1 , list2 )
   list1.each_index do | i |
       list2.each_index do | j |
           if list1[i] == list2[j]
               list1.delete_at ( i )
               list2.delete_at ( j )
           end
       end
   end
   return [ list1 , list2 ]
end

What's weird is that doing this is easy in C (my first language), but
difficult in Ruby. Everything else I've seen has been MUCH easier in Ruby.

Mike Steiner

I would actually need to know what you really want to do :frowning: Let me explain. In a list you can put different elements in the list with the same values. Unlike in a hash, where the keys must be unique.

Do you want to remove the elements that are in the same position on the lists and are equal?

If so I would say:

require 'generator'

list1 = [1,1,2,3,4,6] # => I included the 6 to show what I mean...
list2 = [2,2,3,5,5,6] # => I included the 6 to show what I mean...

def RemoveDupsFromLists ( list1 , list2 )
   lists = SyncEnumerator.new(list1, list2)
   lists.each { |element_list1, element_list2|

     if list1[element_list1] == list2[element_list2]
       list1.delete(element_list1)
       list2.delete(element_list2)
     end
   }
end

Cheers,

Enrique Comba Riepenhausen

···

On 11 May 2007, at 09:10, Robert Klemme wrote:

Sets also come in handy - especially if those lists are large.

Kind regards

  robert

I thought this looked a lot like simplification. I can find a lot of
uses for this simple function.

···

On 5/11/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:

I'm looking for a function that will eliminate all matching items, like
canceling terms in a fraction. If the first list has 3 "a"s, and the second
list has 5 "a"s, then afterwards the first list should have 0 "a"s and the
second list should have 2 "a"s.

Good idea about the & operator for sets, but there could be more than 1
group of matches, so I'd have to iterate:

a = [ 1, 1, 2, 3, 4, 4, 4, 5 ]
b = [ 1, 2, 2, 4, 4, 6, 7 ]
results = a & b
while results
    results.each do | m |
        a = a - m
        b = b - m
    end
    results = a & b
end

I think this will work, and it's sort of elegant (well, not as elegant as
having it built into Ruby already :-)).

Mike Steiner

···

On 5/11/07, Wolfgang Nádasi-Donner <wonado@donnerweb.de> wrote:

> On May 10, 2007, at 10:18 PM, Mike Steiner wrote:
>
>> I'm trying to write some code that removes all elements from 2 lists
that
>> are in both lists. However, I don't want any duplicates from each list
>> deleted also (which is what the array "-" operator does). The code I
have
>> now doesn't handle restarting the current iteration for both loops when
a
>> match is found and deleted in both loops. Here's the code:
>>
>> def RemoveDupsFromLists ( list1 , list2 )
>> list1.each_index do | i |
>> list2.each_index do | j |
>> if list1[i] == list2[j]
>> list1.delete_at ( i )
>> list2.delete_at ( j )
>> end
>> end
>> end
>> return [ list1 , list2 ]
>> end
>>
>> What's weird is that doing this is easy in C (my first language), but
>> difficult in Ruby. Everything else I've seen has been MUCH easier in
>> Ruby.
>>
>> Mike Steiner

Isn't this what you want?

irb(main):001:0> a = %w{a b c d e f g a b c}
=> ["a", "b", "c", "d", "e", "f", "g", "a", "b", "c"]
irb(main):002:0> b = %w{e f g h i j k h i j}
=> ["e", "f", "g", "h", "i", "j", "k", "h", "i", "j"]
irb(main):003:0> a - (a & b)
=> ["a", "b", "c", "d", "a", "b", "c"]
irb(main):004:0> b - (a & b)
=> ["h", "i", "j", "k", "h", "i", "j"]

Wolfgang Nádasi-Donner

Wolfgang Nádasi-Donner schrieb:

Or is it something like this?

...
--- before ---
["a", "b", "a", "a", "c", "a", "b", "c", "d", "e", "f", "e", "a"]
["a", "b", "a", "c", "d", "c", "d", "c", "c", "g", "h", "g", "c"]
--- after ---
["a", "a", "b", "e", "f", "e", "a"]
["d", "c", "c", "g", "h", "g", "c"]

If it is the wanted direction, the program is simple.

class Array
   def remdups(b)
     a= self.dup
     b.each{|el|a.delete_at(a.index(el)) if a.include?(el)}
     a
   end
end

a = %w{a b a a c a b c d e f e a}
b = %w{a b a c d c d c c g h g c}
puts '--- before ---'
p a
p b
puts '--- after ---'
p a.remdups(b)
p b.remdups(a)
p [1,1,1,1].remdups([1,1,2])
p [1,1,2].remdups([1,1,1,1])

Output:

--- before ---
["a", "b", "a", "a", "c", "a", "b", "c", "d", "e", "f", "e", "a"]
["a", "b", "a", "c", "d", "c", "d", "c", "c", "g", "h", "g", "c"]
--- after ---
["a", "a", "b", "e", "f", "e", "a"]
["d", "c", "c", "g", "h", "g", "c"]
[1, 1]
[2]

Wolfgang Nádasi-Donner

Is it safe to delete from lists while you're enumerating through them?

···

On Fri, May 11, 2007 at 04:55:14PM +0900, Enrique Comba Riepenhausen wrote:

Do you want to remove the elements that are in the same position on
the lists and are equal?

If so I would say:

require 'generator'

list1 = [1,1,2,3,4,6] # => I included the 6 to show what I mean...
list2 = [2,2,3,5,5,6] # => I included the 6 to show what I mean...

def RemoveDupsFromLists ( list1 , list2 )
  lists = SyncEnumerator.new(list1, list2)
  lists.each { |element_list1, element_list2|

    if list1[element_list1] == list2[element_list2]
      list1.delete(element_list1)
      list2.delete(element_list2)
    end
  }
end

Wolfgang, that is VERY slick! Well done!

···

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

Mike Steiner wrote:

while results

Well, this while loop will not terminate because is considered true
in ruby, use this instead

while results !=

or the more idiomatic

while not results.empty?

        a = a - m
        b = b - m

I don't think that this '-' works here, you probably meant:

a.delete_at(a.index(m))
b.delete_at(b.index(m))

because a = a - [m] will remove all m's of a and make the code do the
same as:

a, b = a - (a&b), b - (a&b)

although i like the elegance of that solution i prefer something like
this:

def removeDupsFromLists ( list1 , list2 )
  x = list1.dup
  x.each do | i |
    if list2.include?(i)
      list1.delete_at( list1.index(i) )
      list2.delete_at( list2.index(i) )
    end
  end
  return [ list1, list2 ]
end

just because is easier to follow and to debug: there is no way of get an
infinte loop

MoisesMachado

···

--
Posted via http://www.ruby-forum.com/\.