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

...
       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 Feb 14, 2010, at 15:45 , Greg Ma wrote:

puts "#{x.name}-#{x.name.length}"

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.

···

On Sun, Feb 14, 2010 at 4:45 PM, Greg Ma <gregorylepacha@msn.com> wrote:

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

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

···

2010/2/15 Greg Ma <gregorylepacha@msn.com>:

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

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

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

···

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

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 Sun, Feb 14, 2010 at 10:48 PM, Greg Ma <gregorylepacha@msn.com> wrote:

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

···

--
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)

···

On Mon, Feb 15, 2010 at 1:44 AM, Greg Ma <gregorylepacha@msn.com> 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/\.