Compare and delete element from an array

G

Greg Ma

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??... :/

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

Ryan Davis

...
tags.delete_if {
|x| x.name =3D=3D 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:
 
A

Aaron D. Gifford

def remove_tags(array_of_tags)
=A0 =A0if !array_of_tags.empty?
=A0 =A0 =A0array_of_tags.each do |tag|
=A0 =A0 =A0 =A0tags.delete_if {
=A0 =A0 =A0 =A0 =A0|x| x.name =3D=3D tag
=A0 =A0 =A0 =A0 =A0puts x.name + "-"+ x.name.length.to_s
=A0 =A0 =A0 =A0 =A0puts tag + "-"+ tag.length.to_s
=A0 =A0 =A0 =A0 =A0puts "-----"
=A0 =A0 =A0 =A0 =A0}
=A0 =A0 =A0end
=A0 =A0 =A0puts tags
=A0 =A0end
=A0end

Move the line:

x.name =3D=3D 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 =3D=3D 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 =3D=3D 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 =3D=3D 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=3D=3D"two";puts x}
one
two
three
four
five
=3D> ["one", "two", "three", "four", "five"]
irb(main):002:0> ["one", "two", "three", "four", "five"].delete_if{|x|
puts x;x=3D=3D"two"}
one
two
three
four
five
=3D> ["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.
 
G

Greg Ma

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
 
A

Aaron D. Gifford

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

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.
 
G

Greg Ma

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
 
J

Josh Cheek

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
Sounds like a Rails/ActiveRecord question, you'll probably have more luck o=
n
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
http://guides.rubyonrails.org/association_basics.html#has-and-belongs-to-ma=
ny-association-reference

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

Robert Klemme

2010/2/15 Greg Ma said:
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??... :/

#Method in Student Class
def remove_tags(array_of_tags)
=A0 =A0if !array_of_tags.empty?
=A0 =A0 =A0array_of_tags.each do |tag|
=A0 =A0 =A0 =A0tags.delete_if {
=A0 =A0 =A0 =A0 =A0|x| x.name =3D=3D tag
=A0 =A0 =A0 =A0 =A0puts x.name + "-"+ x.name.length.to_s
=A0 =A0 =A0 =A0 =A0puts tag + "-"+ tag.length.to_s
=A0 =A0 =A0 =A0 =A0puts "-----"
=A0 =A0 =A0 =A0 =A0}
=A0 =A0 =A0end
=A0 =A0 =A0puts tags
=A0 =A0end
=A0end

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 =3D 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.e=
mpty?
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

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 

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
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top