Array::index and rindex operator

Discussion in 'Ruby' started by Hadmut Danisch, Jul 5, 2004.

  1. Hi,

    the Array::index and rindex functions

    arr.index( anObject ) -> anInteger or nil
    Return the index of the first/last object in arr such that the
    object == anObject. Returns nil if no match is found.



    Wouldn't it be better to have

    such that anObject === object ?

    regards
    Hadmut
    Hadmut Danisch, Jul 5, 2004
    #1
    1. Advertising

  2. Hadmut Danisch

    George Ogata Guest

    Hadmut Danisch <> writes:

    > Hi,
    >
    > the Array::index and rindex functions
    >
    > arr.index( anObject ) -> anInteger or nil
    > Return the index of the first/last object in arr such that the
    > object == anObject. Returns nil if no match is found.
    >
    >
    >
    > Wouldn't it be better to have
    >
    > such that anObject === object ?



    I'm not so sure about that; #=== is primarily meant for case
    statements, and very few methods use it like the way you propose. It
    would also separate it from other Array/Enumerable methods like #find,
    #delete, #include?, etc.

    Sometimes though I wish that #index could take a block (i.e., return
    the first index for which the block is true). Unless I'm overlooking
    something, there doesn't seem to be a method that does this. I don't
    know what others think of the idea, but that would probably go some
    way towards your goal.
    George Ogata, Jul 6, 2004
    #2
    1. Advertising

  3. George Ogata wrote:
    > Hadmut Danisch <> writes:
    >
    >
    >>Hi,
    >>
    >>the Array::index and rindex functions
    >>
    >> arr.index( anObject ) -> anInteger or nil
    >> Return the index of the first/last object in arr such that the
    >> object == anObject. Returns nil if no match is found.
    >>
    >>
    >>
    >>Wouldn't it be better to have
    >>
    >> such that anObject === object ?

    >
    >
    >
    > I'm not so sure about that; #=== is primarily meant for case
    > statements, and very few methods use it like the way you propose. It
    > would also separate it from other Array/Enumerable methods like #find,
    > #delete, #include?, etc.
    >
    > Sometimes though I wish that #index could take a block (i.e., return
    > the first index for which the block is true). Unless I'm overlooking
    > something, there doesn't seem to be a method that does this. I don't
    > know what others think of the idea, but that would probably go some
    > way towards your goal.


    Enumerable#grep does use #===, so maybe what the OP wants is something
    like grep but instead of returning the matching value returns the index.
    Joel VanderWerf, Jul 6, 2004
    #3
  4. Hi,

    On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:

    > Sometimes though I wish that #index could take a block (i.e., return
    > the first index for which the block is true). Unless I'm overlooking
    > something, there doesn't seem to be a method that does this. I don't
    > know what others think of the idea, but that would probably go some
    > way towards your goal.


    I think that would be nice. I would vote for it.
    You could do this with enumerator, but it is not so compact:

    require "enumerator"

    a = %w(one two three)
    a.enum_for:)each_with_index).find { |s, i| s == "two" }[1]
    => 1

    Regards,
    Kristof
    Kristof Bastiaensen, Jul 6, 2004
    #4
  5. "Kristof Bastiaensen" <> schrieb im Newsbeitrag
    news:p...
    > Hi,
    >
    > On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:
    >
    > > Sometimes though I wish that #index could take a block (i.e., return
    > > the first index for which the block is true). Unless I'm overlooking
    > > something, there doesn't seem to be a method that does this. I don't
    > > know what others think of the idea, but that would probably go some
    > > way towards your goal.

    >
    > I think that would be nice. I would vote for it.
    > You could do this with enumerator, but it is not so compact:
    >
    > require "enumerator"
    >
    > a = %w(one two three)
    > a.enum_for:)each_with_index).find { |s, i| s == "two" }[1]
    > => 1


    Another solution:

    a.inject(0) {|i,s| break i if "two" == s; idx+1}

    #inject is just great!

    Regards

    robert
    Robert Klemme, Jul 6, 2004
    #5
  6. Hi --

    On Tue, 6 Jul 2004, Robert Klemme wrote:

    >
    > "Kristof Bastiaensen" <> schrieb im Newsbeitrag
    > news:p...
    > > Hi,
    > >
    > > On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:
    > >
    > > > Sometimes though I wish that #index could take a block (i.e., return
    > > > the first index for which the block is true). Unless I'm overlooking
    > > > something, there doesn't seem to be a method that does this. I don't
    > > > know what others think of the idea, but that would probably go some
    > > > way towards your goal.

    > >
    > > I think that would be nice. I would vote for it.
    > > You could do this with enumerator, but it is not so compact:
    > >
    > > require "enumerator"
    > >
    > > a = %w(one two three)
    > > a.enum_for:)each_with_index).find { |s, i| s == "two" }[1]
    > > => 1

    >
    > Another solution:
    >
    > a.inject(0) {|i,s| break i if "two" == s; idx+1}


    The problem though (aside from 'idx' for 'i' :) is that you always
    get a number, even if there's no element found:

    [1,2,3].inject(0) {|i,s| break i if "two" == s; i + 1}

    # => 3


    David

    --
    David A. Black
    David A. Black, Jul 6, 2004
    #6
  7. "David A. Black" <> schrieb im Newsbeitrag
    news:pine.LNX.4.44.0407060521080.8383-100000@wobblini...
    > Hi --
    >
    > On Tue, 6 Jul 2004, Robert Klemme wrote:
    >
    > >
    > > "Kristof Bastiaensen" <> schrieb im Newsbeitrag
    > > news:p...
    > > > Hi,
    > > >
    > > > On Tue, 06 Jul 2004 09:42:41 +1000, George Ogata wrote:
    > > >
    > > > > Sometimes though I wish that #index could take a block (i.e.,

    return
    > > > > the first index for which the block is true). Unless I'm

    overlooking
    > > > > something, there doesn't seem to be a method that does this. I

    don't
    > > > > know what others think of the idea, but that would probably go

    some
    > > > > way towards your goal.
    > > >
    > > > I think that would be nice. I would vote for it.
    > > > You could do this with enumerator, but it is not so compact:
    > > >
    > > > require "enumerator"
    > > >
    > > > a = %w(one two three)
    > > > a.enum_for:)each_with_index).find { |s, i| s == "two" }[1]
    > > > => 1

    > >
    > > Another solution:
    > >
    > > a.inject(0) {|i,s| break i if "two" == s; idx+1}

    >
    > The problem though (aside from 'idx' for 'i' :)


    Thanks for that correction!

    > is that you always
    > get a number, even if there's no element found:
    >
    > [1,2,3].inject(0) {|i,s| break i if "two" == s; i + 1}
    >
    > # => 3


    True. The approach is betters suited to a method implementation:

    module Enumerable
    def index_2(obj=nil, &b)
    b = lambda {|x| obj == x} unless b
    inject(0) {|i,el| return i if b.call(el); i+1}
    nil
    end
    end

    Thx for correcting my sloppyness.

    robert
    Robert Klemme, Jul 6, 2004
    #7
  8. Hi --

    On Tue, 6 Jul 2004, Robert Klemme wrote:

    > True. The approach is betters suited to a method implementation:
    >
    > module Enumerable
    > def index_2(obj=nil, &b)
    > b = lambda {|x| obj == x} unless b
    > inject(0) {|i,el| return i if b.call(el); i+1}
    > nil
    > end
    > end


    I agree. My version, for what it's worth, was:

    module Enumerable
    def b_index
    each_with_index {|e,i| return i if yield(e) }
    nil
    end
    end

    I decided to make it orthogonal to #index, rather than a superset of
    it, on the theory that it would be good to avoid making it legal to
    give both an argument and a block. I also like #each_with_index
    here, rather than #inject, only because it does more of the work and
    eliminates the need for manually incrementing an index. I haven't
    benchmarked them though.


    David

    --
    David A. Black
    David A. Black, Jul 6, 2004
    #8
  9. "David A. Black" <> schrieb im Newsbeitrag
    news:pine.LNX.4.44.0407060654550.23516-100000@wobblini...
    > Hi --
    >
    > On Tue, 6 Jul 2004, Robert Klemme wrote:
    >
    > > True. The approach is betters suited to a method implementation:
    > >
    > > module Enumerable
    > > def index_2(obj=nil, &b)
    > > b = lambda {|x| obj == x} unless b
    > > inject(0) {|i,el| return i if b.call(el); i+1}
    > > nil
    > > end
    > > end

    >
    > I agree. My version, for what it's worth, was:
    >
    > module Enumerable
    > def b_index
    > each_with_index {|e,i| return i if yield(e) }
    > nil
    > end
    > end
    >
    > I decided to make it orthogonal to #index, rather than a superset of
    > it, on the theory that it would be good to avoid making it legal to
    > give both an argument and a block.


    Might be best to just enhance #index with the block variant.

    > I also like #each_with_index
    > here, rather than #inject, only because it does more of the work and
    > eliminates the need for manually incrementing an index. I haven't
    > benchmarked them though.


    True. I was blind for that because of my strong #inject affection... :)

    Regards

    robert
    Robert Klemme, Jul 6, 2004
    #9
  10. Hi --

    On Wed, 7 Jul 2004, Robert Klemme wrote:

    >
    > "David A. Black" <> schrieb im Newsbeitrag
    > news:pine.LNX.4.44.0407060654550.23516-100000@wobblini...
    > > Hi --
    > >
    > > On Tue, 6 Jul 2004, Robert Klemme wrote:
    > >
    > > > True. The approach is betters suited to a method implementation:
    > > >
    > > > module Enumerable
    > > > def index_2(obj=nil, &b)
    > > > b = lambda {|x| obj == x} unless b
    > > > inject(0) {|i,el| return i if b.call(el); i+1}
    > > > nil
    > > > end
    > > > end

    > >
    > > I agree. My version, for what it's worth, was:
    > >
    > > module Enumerable
    > > def b_index
    > > each_with_index {|e,i| return i if yield(e) }
    > > nil
    > > end
    > > end
    > >
    > > I decided to make it orthogonal to #index, rather than a superset of
    > > it, on the theory that it would be good to avoid making it legal to
    > > give both an argument and a block.

    >
    > Might be best to just enhance #index with the block variant.


    What stumped me was: given this:

    ary.index(x) {|e| <condition> }

    which behavior would you expect? Or would it raise an exception? I
    couldn't decide which would be best, which is what led me to a new
    method. I can't remember whether there are any methods that protest
    if you give an argument and a block.... I have a memory that there
    are one or two, but I'm not sure what they are.


    David

    --
    David A. Black
    David A. Black, Jul 6, 2004
    #10
  11. "David A. Black" <> schrieb im Newsbeitrag
    news:pine.LNX.4.44.0407060841010.12255-100000@wobblini...
    > Hi --
    >
    > On Wed, 7 Jul 2004, Robert Klemme wrote:
    >
    > >
    > > "David A. Black" <> schrieb im Newsbeitrag
    > > news:pine.LNX.4.44.0407060654550.23516-100000@wobblini...
    > > > Hi --
    > > >
    > > > On Tue, 6 Jul 2004, Robert Klemme wrote:
    > > >
    > > > > True. The approach is betters suited to a method implementation:
    > > > >
    > > > > module Enumerable
    > > > > def index_2(obj=nil, &b)
    > > > > b = lambda {|x| obj == x} unless b
    > > > > inject(0) {|i,el| return i if b.call(el); i+1}
    > > > > nil
    > > > > end
    > > > > end
    > > >
    > > > I agree. My version, for what it's worth, was:
    > > >
    > > > module Enumerable
    > > > def b_index
    > > > each_with_index {|e,i| return i if yield(e) }
    > > > nil
    > > > end
    > > > end
    > > >
    > > > I decided to make it orthogonal to #index, rather than a superset of
    > > > it, on the theory that it would be good to avoid making it legal to
    > > > give both an argument and a block.

    > >
    > > Might be best to just enhance #index with the block variant.

    >
    > What stumped me was: given this:
    >
    > ary.index(x) {|e| <condition> }
    >
    > which behavior would you expect? Or would it raise an exception? I
    > couldn't decide which would be best, which is what led me to a new
    > method.


    #index simply returns nil.

    > I can't remember whether there are any methods that protest
    > if you give an argument and a block.... I have a memory that there
    > are one or two, but I'm not sure what they are.


    Well, what implication would that have? Do you mean to say it's not
    common in Ruby to have methods that either accept a block or an argument?

    Regards

    robert
    Robert Klemme, Jul 6, 2004
    #11
  12. Hadmut Danisch

    Hal Fulton Guest

    Robert Klemme wrote:
    >> I can't remember whether there are any methods that protest
    >>if you give an argument and a block.... I have a memory that there
    >>are one or two, but I'm not sure what they are.

    >
    > Well, what implication would that have? Do you mean to say it's not
    > common in Ruby to have methods that either accept a block or an argument?


    I think it's common to accept a block or an arg, but it raises the
    question of what to do when *both* are given:

    1. raise an exception
    2. do something meaningful

    And I'm not sure whether most core methods do (1) or (2).


    Hal
    Hal Fulton, Jul 6, 2004
    #12
  13. Hi --

    On Wed, 7 Jul 2004, Robert Klemme wrote:

    > > > Might be best to just enhance #index with the block variant.

    > >
    > > What stumped me was: given this:
    > >
    > > ary.index(x) {|e| <condition> }
    > >
    > > which behavior would you expect? Or would it raise an exception? I
    > > couldn't decide which would be best, which is what led me to a new
    > > method.

    >
    > #index simply returns nil.


    I think we're talking about different things. What I meant is: if you
    extend/enhance #index to take a block, and then you give it a block
    *and* an argument, there's an ambiguity (two possible behaviors):

    a = %w{ a b c d }
    i = a.enhanced_index("b") {|x| x == "c" }

    Would i be 1 or 2? Or would it raise an exception, because it's
    ambiguous? (It's different from, say, #inject, where there's both an
    argument and a block but they have a direct connection with each
    other.)

    My conclusion was that it would be better not to allow this to be
    legal, either by enforcing the choice in #index or creating a second
    method.


    David

    --
    David A. Black
    David A. Black, Jul 6, 2004
    #13
  14. Hi --

    On Wed, 7 Jul 2004, Hal Fulton wrote:

    > Robert Klemme wrote:
    > >> I can't remember whether there are any methods that protest
    > >>if you give an argument and a block.... I have a memory that there
    > >>are one or two, but I'm not sure what they are.

    > >
    > > Well, what implication would that have? Do you mean to say it's not
    > > common in Ruby to have methods that either accept a block or an argument?

    >
    > I think it's common to accept a block or an arg, but it raises the
    > question of what to do when *both* are given:
    >
    > 1. raise an exception
    > 2. do something meaningful
    >
    > And I'm not sure whether most core methods do (1) or (2).


    I still have this nagging memory of a method that raises an exception
    when given both, but I can't pin it down.

    The ones I can think of that don't just ignore the block (which of
    course any method will do if it doesn't yield) seem to call the block
    either with the argument (#inject) or with the result of a calculation
    (File.open).


    David

    --
    David A. Black
    David A. Black, Jul 6, 2004
    #14
  15. Hadmut Danisch

    ts Guest

    >>>>> "D" == David A Black <> writes:

    D> I still have this nagging memory of a method that raises an exception
    D> when given both, but I can't pin it down.

    svg% ruby -e 'Hash.new("aa") {}'
    -e:1:in `initialize': wrong number of arguments (ArgumentError)
    from -e:1:in `new'
    from -e:1
    svg%

    svg% ruby -e 'instance_eval("aa") {} '
    -e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
    from -e:1
    svg%



    Guy Decoux
    ts, Jul 6, 2004
    #15
  16. Hadmut Danisch

    George Ogata Guest

    ts <> writes:

    >>>>>> "D" == David A Black <> writes:

    >
    > D> I still have this nagging memory of a method that raises an exception
    > D> when given both, but I can't pin it down.
    >
    > svg% ruby -e 'Hash.new("aa") {}'
    > -e:1:in `initialize': wrong number of arguments (ArgumentError)
    > from -e:1:in `new'
    > from -e:1
    > svg%
    >
    > svg% ruby -e 'instance_eval("aa") {} '
    > -e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
    > from -e:1


    Interestingly:

    g@crash:~$ ruby -e 'Array.new(1, 1){1}'
    -e:1: warning: block supersedes default value argument

    But to answer David( Black)'s concern, I think the only sane choice is
    to raise an exception when both are given. It's ambiguous, so it's an
    error, right?
    George Ogata, Jul 7, 2004
    #16
  17. "David A. Black" <> schrieb im Newsbeitrag
    news:pine.LNX.4.44.0407060917190.8001-100000@wobblini...
    > Hi --
    >
    > On Wed, 7 Jul 2004, Robert Klemme wrote:
    >
    > > > > Might be best to just enhance #index with the block variant.
    > > >
    > > > What stumped me was: given this:
    > > >
    > > > ary.index(x) {|e| <condition> }
    > > >
    > > > which behavior would you expect? Or would it raise an exception? I
    > > > couldn't decide which would be best, which is what led me to a new
    > > > method.

    > >
    > > #index simply returns nil.

    >
    > I think we're talking about different things. What I meant is: if you
    > extend/enhance #index to take a block, and then you give it a block
    > *and* an argument, there's an ambiguity (two possible behaviors):
    >
    > a = %w{ a b c d }
    > i = a.enhanced_index("b") {|x| x == "c" }
    >
    > Would i be 1 or 2? Or would it raise an exception, because it's
    > ambiguous? (It's different from, say, #inject, where there's both an
    > argument and a block but they have a direct connection with each
    > other.)


    I'd certainly have it throw an exception.

    > My conclusion was that it would be better not to allow this to be
    > legal, either by enforcing the choice in #index or creating a second
    > method.


    Yes, enforce the choice would be my preferred solution.

    Regards

    robert
    Robert Klemme, Jul 7, 2004
    #17
  18. Hi --

    On Wed, 7 Jul 2004, George Ogata wrote:

    > ts <> writes:
    >
    > >>>>>> "D" == David A Black <> writes:

    > >
    > > D> I still have this nagging memory of a method that raises an exception
    > > D> when given both, but I can't pin it down.
    > >
    > > svg% ruby -e 'Hash.new("aa") {}'
    > > -e:1:in `initialize': wrong number of arguments (ArgumentError)
    > > from -e:1:in `new'
    > > from -e:1
    > > svg%
    > >
    > > svg% ruby -e 'instance_eval("aa") {} '
    > > -e:1:in `instance_eval': wrong number of arguments (1 for 0) (ArgumentError)
    > > from -e:1

    >
    > Interestingly:
    >
    > g@crash:~$ ruby -e 'Array.new(1, 1){1}'
    > -e:1: warning: block supersedes default value argument
    >
    > But to answer David( Black)'s concern, I think the only sane choice is
    > to raise an exception when both are given. It's ambiguous, so it's an
    > error, right?


    It's an interesting problem, since the existence of #block_given?
    suggests that branching on the presence/absence of a block is legit,
    though these instances show that for this purpose it can be rather
    messy.

    I tend to favor the exception approach, though one could also argue
    for a left-to-right logic: if a method needs an argument xor a block,
    then once it gets an argument, let it be satisfied and ignore the
    block (which is also a legit and indeed default action). One would
    then have to be careful not to have stray useless blocks, but that's
    already the case; one already could, but presumably would not, do
    this:

    /blah/.match(str) { puts "useless block!" }


    David

    --
    David A. Black
    David A. Black, Jul 7, 2004
    #18
  19. Hadmut Danisch

    George Ogata Guest

    "David A. Black" <> writes:

    >> But to answer David( Black)'s concern, I think the only sane choice is
    >> to raise an exception when both are given. It's ambiguous, so it's an
    >> error, right?

    >
    > It's an interesting problem, since the existence of #block_given?
    > suggests that branching on the presence/absence of a block is legit,
    > though these instances show that for this purpose it can be rather
    > messy.
    >
    > I tend to favor the exception approach, though one could also argue
    > for a left-to-right logic: if a method needs an argument xor a block,
    > then once it gets an argument, let it be satisfied and ignore the
    > block (which is also a legit and indeed default action). One would
    > then have to be careful not to have stray useless blocks, but that's
    > already the case; one already could, but presumably would not, do
    > this:
    >
    > /blah/.match(str) { puts "useless block!" }


    Well, one could also counter-argue that in the useless-block cases
    it's not ambiguous, though I think ideally, ruby should still
    complain. I guess it doesn't due to a combination of ruby's dynamic
    nature (it can't determine for itself whether or not a block might be
    needed at compile time, and it's inefficient to check it at runtime),
    and ruby's "no declarations" philosophy (i.e., no declaring "I take
    blocks" in the method definition). Perhaps also because a useless
    block isn't an oft-made mistake.

    As for the "left-to-right" view, there's no precedence for this being
    silently ignored in the presence of ambiguity, is there?
    George Ogata, Jul 7, 2004
    #19
  20. Hi --

    On Wed, 7 Jul 2004, George Ogata wrote:

    > "David A. Black" <> writes:
    >
    > >> But to answer David( Black)'s concern, I think the only sane choice is
    > >> to raise an exception when both are given. It's ambiguous, so it's an
    > >> error, right?

    > >
    > > It's an interesting problem, since the existence of #block_given?
    > > suggests that branching on the presence/absence of a block is legit,
    > > though these instances show that for this purpose it can be rather
    > > messy.
    > >
    > > I tend to favor the exception approach, though one could also argue
    > > for a left-to-right logic: if a method needs an argument xor a block,
    > > then once it gets an argument, let it be satisfied and ignore the
    > > block (which is also a legit and indeed default action). One would
    > > then have to be careful not to have stray useless blocks, but that's
    > > already the case; one already could, but presumably would not, do
    > > this:
    > >
    > > /blah/.match(str) { puts "useless block!" }

    >
    > Well, one could also counter-argue that in the useless-block cases
    > it's not ambiguous, though I think ideally, ruby should still
    > complain. I guess it doesn't due to a combination of ruby's dynamic
    > nature (it can't determine for itself whether or not a block might be
    > needed at compile time, and it's inefficient to check it at runtime),
    > and ruby's "no declarations" philosophy (i.e., no declaring "I take
    > blocks" in the method definition). Perhaps also because a useless
    > block isn't an oft-made mistake.


    I'd rather not have a warning in the general case (i.e., imposed by
    the interpreter rather than by the method's own logic). One might
    have something like:

    def x
    @cached ||= yield * 10
    end

    where the yielding happens conditionally. (Classically non-wonderful
    example, but still :)

    > As for the "left-to-right" view, there's no precedence for this being
    > silently ignored in the presence of ambiguity, is there?


    I don't think any existing methods do this; I'm really just
    speculating about whether or not it would make sense to write a method
    that way. I think it would, at an abstract level, but having the
    method intervene if one calls it ambiguously also makes sense, perhaps
    more sense at the practical level.


    David

    --
    David A. Black
    David A. Black, Jul 7, 2004
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. STEPHEN BECKER I V

    rindex with array of arrays

    STEPHEN BECKER I V, Sep 28, 2004, in forum: Ruby
    Replies:
    3
    Views:
    188
    STEPHEN BECKER I V
    Sep 29, 2004
  2. trans.  (T. Onoma)

    rindex with regexp

    trans. (T. Onoma), Dec 28, 2004, in forum: Ruby
    Replies:
    2
    Views:
    119
    trans. (T. Onoma)
    Dec 28, 2004
  3. Shawn W_
    Replies:
    5
    Views:
    260
    Aldric Giacomoni
    Sep 16, 2009
  4. Paul.Lee.1971

    Rindex used to find end of a string

    Paul.Lee.1971, Feb 28, 2007, in forum: Perl Misc
    Replies:
    4
    Views:
    109
    Dr.Ruud
    Mar 11, 2007
  5. Tomasz Chmielewski

    sorting index-15, index-9, index-110 "the human way"?

    Tomasz Chmielewski, Mar 4, 2008, in forum: Perl Misc
    Replies:
    4
    Views:
    266
    Tomasz Chmielewski
    Mar 4, 2008
Loading...

Share This Page