!x.include? y

basically, I need to filter list A to match list B. list A is a list
of objects with a particular field, and list B is a list of acceptable
values for that field.

what I have works but it reads sort of clumsily. I changed the ! to a
not and got something slightly more readable:

list.reject! {|thing| not list_of_acceptable_values.include? thing.attribute}

is there a nicer way to do it?

···

--
Giles Bowkett
http://www.gilesgoatboy.org

Giles Bowkett wrote:

list.reject! {|thing| not list_of_acceptable_values.include?
thing.attribute}

is there a nicer way to do it?

list.select {|thing| list_of_acceptable_values.include? thing.attribute
} ??

  But then you need to reassign list, since I don't know of any select!

  Vince

···

--
Vincent Fourmond, PhD student
http://vincent.fourmond.neuf.fr/

You could modify the Array class to include an accept! method that
does the opposite of reject! (I'm sure someone else will chime in if
this is wrong or bad practise!)

class Array
  def accept!
    delete_if {|item| not yield item}
  end
end

···

On 29/10/06, Giles Bowkett <gilesb@gmail.com> wrote:

...
list.reject! {|thing| not list_of_acceptable_values.include? thing.attribute}

is there a nicer way to do it?

--
Marcel

Giles Bowkett wrote:

basically, I need to filter list A to match list B. list A is a list
of objects with a particular field, and list B is a list of acceptable
values for that field.

Interesting; you're basically talking about set intersection:
irb(main):001:0> requested = %w|a b c x z|
=> ["a", "b", "c", "x", "z"]
irb(main):002:0> legal = %w|a b c d e f g|
=> ["a", "b", "c", "d", "e", "f", "g"]
irb(main):003:0> requested & legal
=> ["a", "b", "c"]

Except that you're talking about calling considering an object
equivalent to the value of one of its 'fields'. I had hoped that the
following would work, but no luck:
  class Object
    def equiv; self; end
  end

  class Equiv
    def self.( data, equiv )
      self.new( data, equiv )
    end
    attr_accessor :data, :equiv
    def initialize( data, equiv )
      @data, @equiv = data, equiv
    end
    def <=>( o2 ); @equiv <=> o2.equiv; end
    def == ( o2 ); @equiv == o2.equiv; end
    def ===( o2 ); @equiv === o2.equiv; end
    def eq?( o2 ); @equiv.eq?( o2.equiv ); end
    def hash; @equiv.hash end
  end

  possible = [
    Equiv[ 1001, 'a' ],
    Equiv[ 1002, 'b' ],
    Equiv[ 1003, 'c' ],
    Equiv[ 2098, 'x' ],
    Equiv[ 3143, 'z' ]
  ]
  legal = %w|a b c d e f g|

  p possible & legal
  #=>

Looking at the source code for Array#&, I see that it uses the hash
value of at least one of the arrays. (And I can see that my custom hash
method above is getting called.) But that's not enough. Apparently I
can't even *read* C code any more, much less write it.

Can someone who is more C savvy than I am tell me what the following
code is doing (and thus what might be done, if anything, to allow the
built-in Array#& to work with custom classes pretending to be something
they aren't)?

static VALUE
rb_ary_and(ary1, ary2)
    VALUE ary1, ary2;
{
    VALUE hash, ary3, v, vv;
    long i;

    ary2 = to_ary(ary2);
    ary3 = rb_ary_new2(RARRAY(ary1)->len < RARRAY(ary2)->len ?
      RARRAY(ary1)->len : RARRAY(ary2)->len);
    hash = ary_make_hash(ary2, 0);

    for (i=0; i<RARRAY(ary1)->len; i++) {
  v = vv = rb_ary_elt(ary1, i);
  if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
      rb_ary_push(ary3, v);
  }
    }

    return ary3;
}

Hi --

···

On Mon, 30 Oct 2006, Vincent Fourmond wrote:

Giles Bowkett wrote:

list.reject! {|thing| not list_of_acceptable_values.include?
thing.attribute}

is there a nicer way to do it?

list.select {|thing| list_of_acceptable_values.include? thing.attribute
} ??

But then you need to reassign list, since I don't know of any select!

This would be a nice use for delete_unless.

David

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

> ...
> list.reject! {|thing| not list_of_acceptable_values.include? thing.attribute}
>
> is there a nicer way to do it?

You could modify the Array class to include an accept! method that
does the opposite of reject! (I'm sure someone else will chime in if
this is wrong or bad practise!)

class Array
  def accept!
    delete_if {|item| not yield item}
  end
end

this is a very tempting solution. a filter method could be cool too.

···

--
Giles Bowkett
http://www.gilesgoatboy.org

Phrogz wrote:

I had hoped that the following would work, but no luck:

[snip]

The reason that what I posted didn't work is because I mispelled 'eql?'
as 'eq?'. Oops. Following is code that does actually work:
  class Object
    def equiv; self; end
  end

  class Equiv
    def self.( data, equiv )
      self.new( data, equiv )
    end
    attr_accessor :data, :equiv
    def initialize( data, equiv )
      @data, @equiv = data, equiv
    end
    def <=>( o2 ); @equiv <=> o2.equiv end
    def == ( o2 ); @equiv == o2.equiv end
    def ===( o2 ); @equiv === o2.equiv end
    def eql?( o2 ); @equiv.eql?( o2.equiv ) end
    def hash; @equiv.hash end
  end

  possible = [
    Equiv[ 1001, 'a' ],
    Equiv[ 1002, 'b' ],
    Equiv[ 1003, 'c' ],
    Equiv[ 2098, 'x' ],
    Equiv[ 3143, 'z' ]
  ]
  legal = %w|a b c d e f g|

  p possible & legal
  #=> [#<Equiv:0x283203c @equiv="a", @data=1001>, #<Equiv:0x2832000
@equiv="b", @data=1002>, #<Equiv:0x2831fc4 @equiv="c", @data=1003>]

So, going back to the original problem (which has long since been
solved), we can do this instead:

require 'equiv'
objs_as_attributes = list.map{ |obj| Equiv[ obj, obj.attribute ] }
acceptable_obj_wrappers = objs_as_attributes &
list_of_acceptable_values
acceptable_objs = acceptable_obj_wrappers.map{ |eq| eq.data }

Obviously that's too much work. I smell an interesting approach for
automatically treating one type of object as another:

  module Enumerable
    def treat_as( method )
      result = yield map{|obj| Equiv[obj, obj.send(method)]}
      if result.is_a? Equiv
        result.data
      else
        result.map{ |obj| obj.is_a?( Equiv ) ? obj.data : obj }
      end
    end
  end

  Person = Struct.new( :name, :age )
  people = [
    Person.new( 'Gavin', 33 ),
    Person.new( 'Bob', 35 ),
    Person.new( 'Jim', 40 ),
    Person.new( 'Lisa', 32 ),
    Person.new( 'Sam', 30 )
  ]

  legal_ages = [ 32, 33 ]
  legal_people = people.treat_as( :age ){ |people_as_ages|
    people_as_ages & legal_ages
  }
  p legal_people
  #=> [#<struct Person name="Gavin", age=33>, #<struct Person
name="Lisa", age=32>]

  oldest = people.treat_as( :age ){ |people_as_ages| people_as_ages.max
}
  p oldest
  #=> #<struct Person name="Jim", age=40>

This smells wrong applying only to Enumerables, but I'm too busy to
really think this through. Just thought I'd share the idea.

Cheers, I didn't know about delete_unless. (I did find delete_if.)

···

On 10/29/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

Hi --

On Mon, 30 Oct 2006, Vincent Fourmond wrote:

> Giles Bowkett wrote:
>
>> list.reject! {|thing| not list_of_acceptable_values.include?
>> thing.attribute}
>>
>> is there a nicer way to do it?
>
> list.select {|thing| list_of_acceptable_values.include? thing.attribute
> } ??
>
> But then you need to reassign list, since I don't know of any select!

This would be a nice use for delete_unless.

--
Giles Bowkett
http://www.gilesgoatboy.org

I like to modifiy Proc for making definining methods like this easier:

    class Proc
      def inverse
        self.class.new {|*args| !self.call(*args)}
      end
    end

Then of course you could continue down the path Marcel was suggesting:

    class Array
      def select!(&block)
        delete_if &block.inverse
      end
      alias_method :find_all!, :select!
    end

    class Hash
      def select!(&block)
        delete_if &block.inverse
      end
      alias_method :find_all!, :select!
    end

Actually, now this makes me want to think about what other kind of
higher order goodies could be added to Proc =)

···

--
Lou.