Quiz #65, Principle of Great Surprise, and Array.delete sledgehammer

I thought I was actually going to enter my first RubyQuiz, but I've spent three times as much time trying to get Ruby to stop surprising me as I have trying to implement my algorithm, and I've had to abandon my effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
         from (irb):8:in `slice!'
         from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this object in the array, and return to me the array without that object. If the array doesn't have that object, then just give the array back to me."

Why isn't that the Ruby Way? What am I missing here?

Help?

class Array
    def without_one(elem)
      new = self.dup # make a copy so that the original is not changed
      new.delete_at(new.index(elem))
      new
    end
  end

  [1,2,3,4,4,4,5,5].without_one(4) #=> [1,2,3,4,4,5,5]

-Levin

···

On 2/4/06, Dave Howell <groups@grandfenwick.net> wrote:

I mean, this seems so, er, obvious! "Please find one instance of this
object in the array, and return to me the array without that object. If
the array doesn't have that object, then just give the array back to
me."

I was just wanting this method yesterday, and not for the first time. Is there any good reason we can't have a delete_first() method for Array?

James Edward Gray II

···

On Feb 4, 2006, at 2:50 PM, Dave Howell wrote:

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

If I'm understanding you properly, this is one way:

class Array
  def delete_one_element!(value)
    return self unless i = self.index(value)
    self[i] = nil
    self.compact!
  end
end

irb(main):023:0> ar = [1,2,3,8,15,15,8]
=> [1, 2, 3, 8, 15, 15, 8]
irb(main):024:0> ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]
irb(main):025:0> ar.delete_one_element!(99)
=> [1, 2, 3, 8, 15, 8]
irb(main):026:0>

Someone will probably post a solution that uses inject, though. Heh.

···

On 2/4/06, Dave Howell <groups@grandfenwick.net> wrote:

I thought I was actually going to enter my first RubyQuiz, but I've
spent three times as much time trying to get Ruby to stop surprising me
as I have trying to implement my algorithm, and I've had to abandon my
effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single*
element from an array based on equivalence with an object? and get back
said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
         from (irb):8:in `slice!'
         from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this
object in the array, and return to me the array without that object. If
the array doesn't have that object, then just give the array back to
me."

Why isn't that the Ruby Way? What am I missing here?

Help?

harp:~ > cat a.rb
   class Array
     def remove(*args) replace(self - args) end
   end

   a = %w( a b b )
   p a.delete_if{|x| x == "b"}

   a = %w( a b b )
   p(a.delete("b") && a)

   a = %w( a b b )
   p a.select{|x| x != "b"}

   a = %w( a b b )
   p a - ["b"]

   a = %w( a b b c )
   p a.remove("b", "c")

   harp:~ > ruby a.rb
   ["a"]

there are probably more ways.

hth.

-a

···

On Sun, 5 Feb 2006, Dave Howell wrote:

I thought I was actually going to enter my first RubyQuiz, but I've spent three times as much time trying to get Ruby to stop surprising me as I have trying to implement my algorithm, and I've had to abandon my effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
       from (irb):8:in `slice!'
       from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this object in the array, and return to me the array without that object. If the array doesn't have that object, then just give the array back to me."

Why isn't that the Ruby Way? What am I missing here?

Help?

--
happiness is not something ready-made. it comes from your own actions.
- h.h. the 14th dali lama

require 'enumerator'
class Array
def delete_one_element(x = nil)
        if block_given?
           tester = lambda { |item| yield(item) }
        else
            tester = lambda { |item| item == x }
        end
        index_of_element = self.to_enum(:each_index).find { |i| tester.call(self[i]) }
        unless index_of_element.nil?
           self.delete_at(index_of_element)
        end
        self
end

···

On Feb 4, 2006, at 3:50 PM, Dave Howell wrote:

I thought I was actually going to enter my first RubyQuiz, but I've spent three times as much time trying to get Ruby to stop surprising me as I have trying to implement my algorithm, and I've had to abandon my effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
        from (irb):8:in `slice!'
        from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this object in the array, and return to me the array without that object. If the array doesn't have that object, then just give the array back to me."

Why isn't that the Ruby Way? What am I missing here?

Help?

Why isn't that the Ruby Way? What am I missing here?

Help?

You obviously know how to code such a method yourself and i can't answer why for example Array#- doesn't work that way (had to learn that myself).

The shortest solution i came up with is

ar = [1, 2, 3, 8, 15, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(15)])
=> [1, 2, 3, 8, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(14)])
=> [1, 2, 3, 8, 15, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(nil)])
=> [1, 2, 3, 8, 15, 15, 8]

cheers

Simon

Dave Howell wrote:

I thought I was actually going to enter my first RubyQuiz, but I've
spent three times as much time trying to get Ruby to stop surprising me
as I have trying to implement my algorithm, and I've had to abandon my
effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single*
element from an array based on equivalence with an object? and get back
said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
         from (irb):8:in `slice!'
         from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this
object in the array, and return to me the array without that object. If
the array doesn't have that object, then just give the array back to
me."

This may not be of any use to you, but here's a way to delete all
elements but the first:

class Array
  def no_dup!( x )
    (i = index(x)) && delete( x ) && insert(i,x)
    self
  end
end

And here's another version of delete_one_element!.

class Array
  def delete_one_element!( x )
    (i = index(x)) && delete_at( i )
    self
  end
end

class Enumerable
  def delete_one_of(obj)
    self.inject(,false) { |(arr, already_deleted), elem|
      arr << elem unless elem == obj and not already_deleted
      already_deleted = true if elem == obj
      [arr, already_deleted]
    }[0]
  end
end

... this is untested

-Levin, scnr

···

On 2/4/06, Wilson Bilkovich <wilsonb@gmail.com> wrote:

On 2/4/06, Dave Howell <groups@grandfenwick.net> wrote:

Someone will probably post a solution that uses inject, though. Heh.

Why isn't that the Ruby Way? What am I missing here?
Help?

You obviously know how to code such a method yourself and i can't answer why for example Array#- doesn't work that way (had to learn that myself).

Well, actually, no, I don't. Here's what I bashed together:

class Array
  def delete_single(element)
    if index=self.index(element) then
      self.slice!(index,1)
      self
    else
      nil
    end
  end
end

It was originally going to be similar to .delete until I realized that .delete didn't return the array, but rather, everything *but* the array. And it had an optional "yield." I'm mystified as to how to make THAT part work, since neither ri nor the Pickaxe book gave me any hint at all as to how to detect IF a block is present, and yield if it is. Just using "yield" got me an error since I wasn't using a block in this case.

And even the above code took me hours to figure out, and I don't really understand why it works. Well, that's not true, but I don't understand it well enough to be able to figure out if there's a shorter way to do it. I got that far by noticing what Pickaxe said about Array.slice!

  Equivalent to
    def slice!(*args)
      result = self[*args]
      self[*args] = nil
      result
    end

which basically whispered to me "you have to return something other than the result of your deletion...." So I tried a couple things until something worked. {shrug}

The shortest solution i came up with is

ar = [1, 2, 3, 8, 15, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(15)])
=> [1, 2, 3, 8, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(14)])
=> [1, 2, 3, 8, 15, 15, 8]

p ar.values_at(*(0...ar.size).to_a - [ar.index(nil)])
=> [1, 2, 3, 8, 15, 15, 8]

Wow. I don't understand this one at all. The "-" operator also was massively destructive, removing all duplicates when used. I'm going to have to look up the * operator (as I recall, it's one of those really strange-and-clever Ruby things) to see what might be going on here.

Oh! Oh oh oh!

My, that's very clever. Create an array containing the *index values* of the "real" array. They are, of course, each unique. Array-subtract the index value for the element you want to remove. Array-subtracting "nil" has no effect but isn't an error. Then use the remaining index values to copy those elements out of the original "real" array.

Although I'm still going to have to look up what the * is accomplishing. :slight_smile:

···

On Feb 4, 2006, at 13:22, Simon Kröger wrote:

Aha!

<checks index of Pickaxe>

I see. It's in there, but buried near the end of a section in the middle of the discussion on blocks.

Time for some more highlighting....

···

On Feb 4, 2006, at 13:10, Logan Capaldo wrote:

       if block_given?

Wouldn't delete_first still ought to return "_obj_ or nil"?

···

On Feb 4, 2006, at 13:09, James Edward Gray II wrote:

On Feb 4, 2006, at 2:50 PM, Dave Howell wrote:

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

I was just wanting this method yesterday, and not for the first time. Is there any good reason we can't have a delete_first() method for Array?

You are, but it isn't. I might well have "nil" as a legitimate item in the array, and .compact would erase all of them. I definitely want a solution that can _never_ reduce the size of my array by more than one element.

···

On Feb 4, 2006, at 13:10, Wilson Bilkovich wrote:

If I'm understanding you properly, this is one way:

class Array
  def delete_one_element!(value)
    return self unless i = self.index(value)
    self[i] = nil
    self.compact!
  end
end

This is just a list of many other ways to not do what I want, yes? <scratch head>

···

On Feb 4, 2006, at 13:10, ara.t.howard@noaa.gov wrote:

On Sun, 5 Feb 2006, Dave Howell wrote:

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

  class Array
    def remove(*args) replace(self - args) end
  end

  a = %w( a b b )
  p a.delete_if{|x| x == "b"}
  p(a.delete("b") && a)
  p a.select{|x| x != "b"}
  p a - ["b"]
  p a.remove("b", "c")

all return
  ["a"]

William James wrote:

Dave Howell wrote:
  

I thought I was actually going to enter my first RubyQuiz, but I've
spent three times as much time trying to get Ruby to stop surprising me
as I have trying to implement my algorithm, and I've had to abandon my
effort since I don't have the time to spend. Sigh.

Can somebody explain to me how I'm supposed to delete a *single*
element from an array based on equivalence with an object? and get back
said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

Too enthusiastic....
ar.delete(15)
=> 15
ar
=> [1, 2, 3, 8, 8]

Right results, wrong output...
ar.slice!(ar.index(15))
=> 15
ar
=> [1, 2, 3, 8, 15, 8]

and also surprised me by not handling 'nil' as I wanted...
ar.slice!(ar.index(13))
TypeError: no implicit conversion from nil to integer
         from (irb):8:in `slice!'
         from (irb):8

I mean, this seems so, er, obvious! "Please find one instance of this
object in the array, and return to me the array without that object. If
the array doesn't have that object, then just give the array back to
me."
    
This may not be of any use to you, but here's a way to delete all
elements but the first:

class Array
  def no_dup!( x )
    (i = index(x)) && delete( x ) && insert(i,x)
    self
  end
end

And here's another version of delete_one_element!.

class Array
  def delete_one_element!( x )
    (i = index(x)) && delete_at( i )
    self
  end
end

And here's yet another version, similar to the ones already posted though:

class Array
  def delete_one! n
    (i = index(n)) ? delete_at(i) : nil
  end
end

Array.uniq!

???

James Edward Gray II

···

On Feb 5, 2006, at 5:28 AM, William James wrote:

This may not be of any use to you, but here's a way to delete all
elements but the first:

class Array
  def no_dup!( x )
    (i = index(x)) && delete( x ) && insert(i,x)
    self
  end
end

Dave Howell wrote:

And it had an optional "yield." I'm mystified as to how to make THAT
part work, since neither ri nor the Pickaxe book gave me any hint at all
as to how to detect IF a block is present, and yield if it is. Just
using "yield" got me an error since I wasn't using a block in this case.

Use Kernel.block_given?, like this:

def one_two_three
  if block_given?; yield 1; yield 2; yield 3;
  else [1,2,3]
  end
end

See the PickAxe, in the chapter on blocks and iterators, page 51.

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Good point. You are probably right.

James Edward Gray II

···

On Feb 4, 2006, at 3:49 PM, Dave Howell wrote:

On Feb 4, 2006, at 13:09, James Edward Gray II wrote:

On Feb 4, 2006, at 2:50 PM, Dave Howell wrote:

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

I was just wanting this method yesterday, and not for the first time. Is there any good reason we can't have a delete_first() method for Array?

Wouldn't delete_first still ought to return "_obj_ or nil"?

OK.. Here's a safer version:
class Array
  def delete_first(value)
    return self unless i = self.index(value)
    self.delete_at(i)
    self
  end
end

irb(main):013:0> a = [1,2,3,8,15,15,8]
=> [1, 2, 3, 8, 15, 15, 8]
irb(main):014:0> a.delete_first(15)
=> [1, 2, 3, 8, 15, 8]
irb(main):015:0>

···

On 2/4/06, Dave Howell <groups@grandfenwick.net> wrote:

On Feb 4, 2006, at 13:10, Wilson Bilkovich wrote:

> If I'm understanding you properly, this is one way:
>
> class Array
> def delete_one_element!(value)
> return self unless i = self.index(value)
> self[i] = nil
> self.compact!
> end
> end
>

You are, but it isn't. I might well have "nil" as a legitimate item in
the array, and .compact would erase all of them. I definitely want a
solution that can _never_ reduce the size of my array by more than one
element.

sorry. didn't read closely enough:

     harp:~ > cat a.rb
     class Array
       def remove(elem) delete_at(index(elem)) rescue nil; self end
     end

     a = [1, 2, 3, 8, 15, 15, 8]
     p a.remove(15)

     harp:~ > ruby a.rb
     [1, 2, 3, 8, 15, 8]

though i'll say that not raising an error when the element is not found is an
amazingly bad idea that would mask many bugs (imagine an algorithm that should
sometimes remove things but __never__ does)

regards.

-a

···

On Sun, 5 Feb 2006, Dave Howell wrote:

On Feb 4, 2006, at 13:10, ara.t.howard@noaa.gov wrote:

On Sun, 5 Feb 2006, Dave Howell wrote:

Can somebody explain to me how I'm supposed to delete a *single* element from an array based on equivalence with an object? and get back said array with said element deleted?

In other words....

ar = [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(15)
=> [1, 2, 3, 8, 15, 8]

ar.delete_one_element!(14)
=> [1, 2, 3, 8, 15, 15, 8]

ar.delete_one_element!(nil)
=> [1, 2, 3, 8, 15, 15, 8]

I did find so many different ways of not doing this....

  class Array
    def remove(*args) replace(self - args) end
  end

  a = %w( a b b )
  p a.delete_if{|x| x == "b"}
  p(a.delete("b") && a)
  p a.select{|x| x != "b"}
  p a - ["b"]
  p a.remove("b", "c")

all return
  ["a"]

This is just a list of many other ways to not do what I want, yes? <scratch >

--
happiness is not something ready-made. it comes from your own actions.
- h.h. the 14th dali lama