In article <p86Y6.80058$Ne5.3134820 / e420r-sjo3.usenetserver.com>, "Niklas
Frykholm" <niklas / kagi.com> wrote:


> On Thu, Jun 21, 2001 at 02:56:51AM +0900, Hugh Sasse Staff Elec Eng
> wrote:
>> Is there an elegant way to do a bag diffeence between 2 arrays, rather
>> than a set difference?
>> [1,1,1,2,3,3,4,4,4] <something> [1,2,3,4,4] gives: [1,1,3,4]
>> If ordering is preserved that would be great.
> Don't know if this is the most elegant solution, but it is the first
> that comes to my mind.
> 
>     class Array
>         def with_count
>             h = {}; h.default = 0
>             self.collect {|x| [x, h[x] += 1]}
>         end
>         def bagminus(other)
>             (self.with_count - other.with_count).collect {|x| x[0]}
>         end
>     end
> 
>     a = [1,1,1,2,3,3,4,4,4]
>     b = [1,2,3,4,4]
> 
>     p a.bagminus b # --> [1, 1, 3, 4]
> // Niklas

I plan to read this carefully when I have time, but here's what I came up
with.  It's slightly more verbose, but I think an Array#delete_first()
would be useful, which is a bonus.  How efficient it is depends on how
efficient Array#index and Array#delete_at are, but I would guess they're
very simple.

class Array
 
    def delete_first(x)
 
        if (i = self.index(x))
            self.delete_at(i)
        end
 
    end
 
    def bag_minus(other)
 
        difference = Array.new
        unwanted  = other.clone
 
        self.each do |x|
            if !unwanted.delete_first(x)
                difference.push(x)
            end
        end
 
        return difference
    end
 
end
 
a = [0,0,1,1,1,2,2,3,3,3,3,4,4,5,5,6]
b = [1,2,3,3,4]
 
puts "#{a}.bag_minus(#{b}) = #{a.bag_minus(b)}" # -->
[0,0,1,1,2,3,3,4,5,5,6]