Compare and delete element from an array

Hi,
I try to compare an arroy of Tag and an array of string. I would like to
remove from my array of Tag all element that are in the array of string.
I,ve done the method below that should be working fine. When i look at
the console result, the element are equals, so is should be deleted but
it is not. I donc understand why??.. :confused:

#Method in Student Class
def remove_tags(array_of_tags)
if !array_of_tags.empty?
array_of_tags.each do |tag|
tags.delete_if {
|x| x.name == tag
puts x.name + “-”+ x.name.length.to_s
puts tag + “-”+ tag.length.to_s
puts “-----”
}
end
puts tags
end
end

#result
Completed in 95ms (View: 83, DB: 5) | 200 OK
[http://localhost/students/1/edit]
moi-3
moi-3

greg-4
moi-3

#Tag:0x10442cf98
#Tag:0x10442ced0

Here we see that the first element should be deleted, but when i display
the array, it has not…

On Feb 14, 2010, at 15:45 , Greg Ma wrote:


tags.delete_if {
|x| x.name == tag
puts x.name + “-”+ x.name.length.to_s
puts tag + “-”+ tag.length.to_s
puts “-----”
}

Here we see that the first element should be deleted, but when i display
the array, it has not…

delete_if evaluates the block to see if it should delete the element or
not. you’re debugging with puts (a bad idea to begin with) and puts
returns nil. So you’re never going to delete anything.

I’d also recommend using string interpolation:

puts x.name + “-”+ x.name.length.to_s

vs:

On Sun, Feb 14, 2010 at 4:45 PM, Greg Ma [email protected] wrote:

 puts tags

end
end

Move the line:

x.name == tag

to the end of the block:

def remove_tags(array_of_tags)
if !array_of_tags.empty?
array_of_tags.each do |tag|
tags.delete_if {|x|
puts x.name + “-”+ x.name.length.to_s
puts tag + “-”+ tag.length.to_s
puts “-----”
x.name == tag
}
end
puts tags
end
end

The Array#delete_if() method only removes array elements IF the block
evaluates to true. With the “x.name == tag” test positioned at the
beginning of the block, it is effectively ignored, since the last
expression in the block will be the block’s return value. You were
returning whatever the ‘puts “-----”’ method returns–nil in this
case, which is considered “false” and so no elements would ever be
deleted. By moving the “x.name == tag” expression to the bottom of
the block, you will return a boolean result of the comparison.

Here’s another example of this behavior:
irb(main):001:0> [“one”, “two”, “three”, “four”, “five”].delete_if{|x|
x==“two”;puts x}
one
two
three
four
five
=> [“one”, “two”, “three”, “four”, “five”]
irb(main):002:0> [“one”, “two”, “three”, “four”, “five”].delete_if{|x|
puts x;x==“two”}
one
two
three
four
five
=> [“one”, “three”, “four”, “five”]

Note that the first version with a “puts” at the end of the delete_if
block failed to remove element “two”, but reordering the operation in
the second version worked.

Aaron out.

Note that the first version with a “puts” at the end of the delete_if
block failed to remove element “two”, but reordering the operation in
the second version worked.

Even if i remove the puts, it still doesnt remove anything.

def remove_tags(array_of_tags)
if !array_of_tags.empty?
array_of_tags.each do |tag|
tags.delete_if { |x| x.name == tag }
end
end
end

Thanks for the explanation!
I finally found my error. In fact the delete_if was correcty working but
the modification from the array wasnt reproduce on the database.
What am i missing to do so?

Greg

On Sun, Feb 14, 2010 at 10:48 PM, Greg Ma [email protected]
wrote:

   tags.delete_if { |x| x.name == tag }
 end

end
end

You call the “delete_if()” method on the “tags” variable–where is
tags defined? You’ve fixed one bug (and it was indeed a bug). If your
code isn’t working now, it likely has to do with the scope where tags
is defined. In your original post you didn’t mention getting a
NameError “undefined local variable or method `tags’” exception, so
unless your code (or whatever code executes your code) rescues such an
exception silently, I assume that “tags” IS in fact defined
somehow/somewhere.

Since I don’t know how your “tags” array or Student class works, I
contrived something that’s probably very, very different, but may give
you an idea where to look in your own code for your bug:

CODE STARTS

class Tag
def initialize(name)
@name = name
end
attr_accessor :name

def to_s
“TAG(#{@name})”
end
end

class Student
def initialize()
@tag_list = []
end

Add a single tag to the Student:

def add_tags(array_of_tags)
array_of_tags.each do |tag_name|
@tag_list << Tag.new(tag_name)
end
end

Remove multiple tags from the Student:

def remove_tags(array_of_tags)
array_of_tags.each do |tag_name|
@tag_list.delete_if { |x| x.name == tag_name }
end
end

def to_s
“STUDENT(” + @tag_list.map{ |tag| tag.to_s }.join(“,”) + “)”
end
end

jane_doe = Student.new
jane_doe.add_tags([“intelligent”, “lazy”, “geek”, “irresponsible”,
“nerd”, “smart”])

puts “Jane has these tags:”
puts jane_doe.to_s

tags_to_remove = [ “lazy”, “irresponsible” ]

puts “Removing tags with these names:”
puts " " + tags_to_remove.join(“,”)

Remove the tags:

jane_doe.remove_tags(tags_to_remove)

puts “After removal, Jane has these tags:”
puts jane_doe.to_s

CODE ENDS

SAMPLE OUTPUT:

Jane has these tags:
STUDENT(TAG(intelligent),TAG(lazy),TAG(geek),TAG(irresponsible),TAG(nerd),TAG(smart))
Removing tags with these names:
lazy,irresponsible
After removal, Jane has these tags:
STUDENT(TAG(intelligent),TAG(geek),TAG(nerd),TAG(smart))

OUTPUT ENDS

In the above contrived sample, notice that the Student#remove_tags()
method is a bit different than your method. That’s because I defined
the equiv. of your “tags” variable as a Student class instance
variable “@tag_list” instead. Also I omitted the “if” statement “if
!@tag_list.empty?” (or “if !tags.empty?” in your code) because the
each method will not execute the block at all if the array is empty to
begin with–it’s redundant and not needed.

I hope this gives you an idea of where in your own code to look to debug
it.

Aaron out.

On Mon, Feb 15, 2010 at 1:44 AM, Greg Ma [email protected] wrote:

Thanks for the explanation!
I finally found my error. In fact the delete_if was correcty working but
the modification from the array wasnt reproduce on the database.
What am i missing to do so?

Greg

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

Sounds like a Rails/ActiveRecord question, you’ll probably have more
luck on
that mailing list
http://groups.google.com/group/rubyonrails-talk

But for a good place to start, I assume you have a
has_and_belongs_to_many
association, in which case the guides imply you can just use delete

4.4.1.4 collection.delete(object, …)
The collection.delete method removes one or more objects from the
collection
by deleting records in the join table. This does not destroy the
objects.
@part.assemblies.delete(@assembly1)

2010/2/15 Greg Ma [email protected]:

 array_of_tags.each do |tag|
   tags.delete_if {
     |x| x.name == tag
     puts x.name + "-"+ x.name.length.to_s
     puts tag + "-"+ tag.length.to_s
     puts "-----"
     }
 end
 puts tags

end
end

This algorithm has roughly effort O(n^2) because you compare all pairs
of elements.

I don’t know sizes of your data structures but here’s how I’d do it

require ‘set’

def remove_tags(array_of_tags)
unless array_of_tags.empty?
aot = array_of_tags.to_set
tags.delete_if {|t| aot.include? t.name}
end

self
end

The advantage is that Set has O(1) lookup because it uses hashing
internally. It may not pay off for small data structures so you could
as well do

def remove_tags(array_of_tags)
tags.delete_if {|t| array_of_tags.include? t.name} unless
array_of_tags.empty?
self
end

This one still has O(n^2) but since Array#include? is used which is
implemented in C chances are that is faster than the variant with Set
because the Set creation overhead is removed.

Kind regards

robert