Looping through array, deleting elements

Discussion in 'Ruby' started by dkmd_nielsen, Mar 8, 2007.

  1. dkmd_nielsen

    dkmd_nielsen Guest

    My idea of looping through an array, interrogating elements that are
    of class instruction, and deleting the element does not appear to work
    as I thought. I think I know why. What is the best solution?

    block.each {|i| ab.delete(i) if i.parm == "FOO"}

    where block is a series of elements of class instruction(i). Class
    instruction has two attributes, parm and arg. If the instruction parm
    is FOO, then delete the instruction object from the array and keep
    looping. Half of what should be deleted actually get's deleted. I
    think the reason is: the loop is looking at element x foo, element x
    foo get's deleted, the array is shifted left, array pointer has not
    changed but is now referencing element y foo, loop advances the array
    pointer, and now array pointer is pointing at element z foo.

    If what I think is correct, what is the best solution? Do I set the
    current element to nil, then compact the array after the loop? Do I
    delete the instruction and then do a redo? Or is there a better
    solution than those?

    Thanks,
    dvn
     
    dkmd_nielsen, Mar 8, 2007
    #1
    1. Advertising

  2. dkmd_nielsen

    Tim Hunter Guest

    dkmd_nielsen wrote:
    > My idea of looping through an array, interrogating elements that are
    > of class instruction, and deleting the element does not appear to work
    > as I thought. I think I know why. What is the best solution?


    What about the Array#delete_if method?


    --
    Posted via http://www.ruby-forum.com/.
     
    Tim Hunter, Mar 8, 2007
    #2
    1. Advertising

  3. On 08/03/07, dkmd_nielsen <> wrote:
    > My idea of looping through an array, interrogating elements that are
    > of class instruction, and deleting the element does not appear to work
    > as I thought. I think I know why. What is the best solution?
    >
    > block.each {|i| ab.delete(i) if i.parm == "FOO"}
    >
    > where block is a series of elements of class instruction(i). Class
    > instruction has two attributes, parm and arg. If the instruction parm
    > is FOO, then delete the instruction object from the array and keep
    > looping. Half of what should be deleted actually get's deleted. I
    > think the reason is: the loop is looking at element x foo, element x
    > foo get's deleted, the array is shifted left, array pointer has not
    > changed but is now referencing element y foo, loop advances the array
    > pointer, and now array pointer is pointing at element z foo.
    >
    > If what I think is correct, what is the best solution? Do I set the
    > current element to nil, then compact the array after the loop? Do I
    > delete the instruction and then do a redo? Or is there a better
    > solution than those?
    >
    > Thanks,
    > dvn


    This sounds like it would be a lot easier with Array#reject or Array#select.
     
    Farrel Lifson, Mar 8, 2007
    #3
  4. Alle gioved=EC 8 marzo 2007, dkmd_nielsen ha scritto:
    > My idea of looping through an array, interrogating elements that are
    > of class instruction, and deleting the element does not appear to work
    > as I thought. I think I know why. What is the best solution?
    >
    > block.each {|i| ab.delete(i) if i.parm =3D=3D "FOO"}
    >
    > where block is a series of elements of class instruction(i). Class
    > instruction has two attributes, parm and arg. If the instruction parm
    > is FOO, then delete the instruction object from the array and keep
    > looping. Half of what should be deleted actually get's deleted. I
    > think the reason is: the loop is looking at element x foo, element x
    > foo get's deleted, the array is shifted left, array pointer has not
    > changed but is now referencing element y foo, loop advances the array
    > pointer, and now array pointer is pointing at element z foo.
    >
    > If what I think is correct, what is the best solution? Do I set the
    > current element to nil, then compact the array after the loop? Do I
    > delete the instruction and then do a redo? Or is there a better
    > solution than those?
    >
    > Thanks,
    > dvn


    I'm not sure I understand your code (what is that ab in the each block?) At=
    =20
    any rate, there's method which directly deletes the elements of an array=20
    which satisfy a given condition: Array#delete_if:

    a=3D[1,2,3,4,5,6]
    a.delete_if{|i| i > 3}
    =3D> [1,2,3]

    This method passes each element of the array to the block, then deletes it =
    if=20
    the block returns true.

    Stefano
     
    Stefano Crocco, Mar 8, 2007
    #4
  5. dkmd_nielsen

    Tim Pease Guest

    On 3/8/07, dkmd_nielsen <> wrote:
    > My idea of looping through an array, interrogating elements that are
    > of class instruction, and deleting the element does not appear to work
    > as I thought. I think I know why. What is the best solution?
    >
    > block.each {|i| ab.delete(i) if i.parm == "FOO"}
    >


    Not a good idea! You are modifying the array as you iterate over it
    -- this will cause some unexpected behavior:

    ary = [1,2,3,4]
    ary.each {|x| ary.delete(x) if x > 2}

    p ary #=> [1, 2, 4]

    oops!

    > where block is a series of elements of class instruction(i). Class
    > instruction has two attributes, parm and arg. If the instruction parm
    > is FOO, then delete the instruction object from the array and keep
    > looping. Half of what should be deleted actually get's deleted. I
    > think the reason is: the loop is looking at element x foo, element x
    > foo get's deleted, the array is shifted left, array pointer has not
    > changed but is now referencing element y foo, loop advances the array
    > pointer, and now array pointer is pointing at element z foo.
    >


    You are correct. What happens is that we delete the number 3 from the
    array and this compacts the array -- 4 shifts into the position where
    3 just was. Now the "each" method moves the index to the next position
    thereby skipping the number 4.

    > If what I think is correct, what is the best solution? Do I set the
    > current element to nil, then compact the array after the loop? Do I
    > delete the instruction and then do a redo? Or is there a better
    > solution than those?
    >


    Use the delete_if method on the array.

    ary.delete_if {|x| x > 2}

    p ary #=> [1,2]

    Blessings,
    TwP
     
    Tim Pease, Mar 8, 2007
    #5
  6. dkmd_nielsen

    dkmd_nielsen Guest

    On Mar 8, 3:02 pm, "Tim Pease" <> wrote:
    > On 3/8/07, dkmd_nielsen <> wrote:
    >
    > > My idea of looping through an array, interrogating elements that are
    > > of class instruction, and deleting the element does not appear to work
    > > as I thought. I think I know why. What is the best solution?

    >
    > > block.each {|i| ab.delete(i) if i.parm == "FOO"}

    >
    > Not a good idea! You are modifying the array as you iterate over it
    > -- this will cause some unexpected behavior:
    >
    > ary = [1,2,3,4]
    > ary.each {|x| ary.delete(x) if x > 2}
    >
    > p ary #=> [1, 2, 4]
    >
    > oops!
    >
    > > where block is a series of elements of class instruction(i). Class
    > > instruction has two attributes, parm and arg. If the instruction parm
    > > is FOO, then delete the instruction object from the array and keep
    > > looping. Half of what should be deleted actually get's deleted. I
    > > think the reason is: the loop is looking at element x foo, element x
    > > foo get's deleted, the array is shifted left, array pointer has not
    > > changed but is now referencing element y foo, loop advances the array
    > > pointer, and now array pointer is pointing at element z foo.

    >
    > You are correct. What happens is that we delete the number 3 from the
    > array and this compacts the array -- 4 shifts into the position where
    > 3 just was. Now the "each" method moves the index to the next position
    > thereby skipping the number 4.
    >
    > > If what I think is correct, what is the best solution? Do I set the
    > > current element to nil, then compact the array after the loop? Do I
    > > delete the instruction and then do a redo? Or is there a better
    > > solution than those?

    >
    > Use the delete_if method on the array.
    >
    > ary.delete_if {|x| x > 2}
    >
    > p ary #=> [1,2]
    >
    > Blessings,
    > TwP


    There are a couple of dumb things I have done. First, I had omitted
    that block is an object, not of class array. So its each method is
    iterating through an array internal to it. Nice little detail to
    omit. Duh. Second, there is something called "retry". I was aware
    of break, next, and redo, but I was unware of "retry." It worked
    great.

    I looked at the delete_if. What I could do with that is create a
    delete_if method in the class block that implements delete_if. In the
    long run, I think that is the best thing to do.

    Thanks for everything. One always finds the answer on his/her own
    shortly after asking for help.

    dvn
     
    dkmd_nielsen, Mar 8, 2007
    #6
    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. Replies:
    16
    Views:
    856
    James Kanze
    Apr 12, 2007
  2. Andy
    Replies:
    3
    Views:
    501
    James Kanze
    Jun 8, 2007
  3. Nate St.germain
    Replies:
    2
    Views:
    135
    Nate St.Germain
    Jun 1, 2010
  4. Aaron
    Replies:
    2
    Views:
    577
    dhtml
    Apr 10, 2011
  5. Replies:
    5
    Views:
    305
Loading...

Share This Page