Noob 'why doesn't this work' question

Hi and greetings to all group members!

I’m a new Ruby user with a background in Applescript, Hypertalk
(Supercard), and a little Unix. I was intrigued by Matt N.‘s
article about Applescript and Ruby, and that’s what got me reading some
intro books on Ruby. I just bought Textmate and I am lovin’ it!

Anyway, I am wondering why this seemingly simple script fails:

sizeList = [0,1,2,3,4,5,6]
countarray = [3,4,6]
countarray.each {|x| sizeList.delete_at(x)}

what I want to happen is that sizelist gets deleted at the positions
specified in countArray, but the result I am getting is:

0
1
2
4
6

and the expected result should be 0,1,2,5, right?
What gives?

TIA,
vince

Mac OS X 10.5.2, G5 Quad, Textmate editor, ruby 1.86

Vincent A. wrote:

Hi and greetings to all group members!

I’m a new Ruby user with a background in Applescript, Hypertalk
(Supercard), and a little Unix. I was intrigued by Matt N.‘s
article about Applescript and Ruby, and that’s what got me reading some
intro books on Ruby. I just bought Textmate and I am lovin’ it!

Anyway, I am wondering why this seemingly simple script fails:

sizeList = [0,1,2,3,4,5,6]
countarray = [3,4,6]
countarray.each {|x| sizeList.delete_at(x)}

and the expected result should be 0,1,2,5, right?
What gives?

Start counting at 0

On Mon, Apr 14, 2008 at 5:47 PM, Vincent A. [email protected]
wrote:

countarray = [3,4,6]

and the expected result should be 0,1,2,5, right?
What gives?

Every time you delete an element, all the values to the right are
shifted one position. What you are doing essentially is:

irb(main):001:0> a = [0,1,2,3,4,5,6]
=> [0, 1, 2, 3, 4, 5, 6]
irb(main):002:0> a.delete_at(3)
=> 3
irb(main):003:0> a
=> [0, 1, 2, 4, 5, 6]
irb(main):004:0> a.delete_at(4)
=> 5
irb(main):005:0> a
=> [0, 1, 2, 4, 6]
irb(main):006:0> a.delete_at(6)
=> nil
irb(main):007:0> a
=> [0, 1, 2, 4, 6]

After the first delete_at, the elements 4,5 and 6 are shifted to fill
the space
of the deleted element. So now, the element at index 4 is not the 4,
it’s the 5.
After those two deletions, the array no longer holds an element with
index 6, so
the last deletion does nothing.

Hope this helps,

Jesus.

On Apr 14, 12:47 pm, Vincent A. [email protected] wrote:

countarray = [3,4,6]

and the expected result should be 0,1,2,5, right?
What gives?

Actually no, when you #delete_at one element from an array, the
indexes of it get altered too:

You can interpret what is doing #each on countarray:

irb(main):001:0> sizeList = [0,1,2,3,4,5,6]
=> [0, 1, 2, 3, 4, 5, 6]
irb(main):002:0> sizeList.delete_at(3)
=> 3
irb(main):003:0> sizeList.delete_at(4)
=> 5
irb(main):004:0> sizeList.delete_at(6)
=> nil

So, each time you iterate over countarray and remove the indicated
position, sizeList gets updated, and thus, the next items in the
countarray no longer reference your expected positions.

Errr, sound complicated my explanation, anyone with a better written
one? :slight_smile:

HTH,

Luis L. wrote:

On Apr 14, 12:47 pm, Vincent A. [email protected] wrote:

countarray = [3,4,6]

and the expected result should be 0,1,2,5, right?
What gives?

Actually no, when you #delete_at one element from an array, the
indexes of it get altered too:

You can interpret what is doing #each on countarray:

irb(main):001:0> sizeList = [0,1,2,3,4,5,6]
=> [0, 1, 2, 3, 4, 5, 6]
irb(main):002:0> sizeList.delete_at(3)
=> 3
irb(main):003:0> sizeList.delete_at(4)
=> 5
irb(main):004:0> sizeList.delete_at(6)
=> nil

So, each time you iterate over countarray and remove the indicated
position, sizeList gets updated, and thus, the next items in the
countarray no longer reference your expected positions.

Errr, sound complicated my explanation, anyone with a better written
one? :slight_smile:

HTH,

Ah, yes. I knew it was something simple.
I’ll just sort countarray so that the highest index value is first, and
perhaps that will take care of the problem!
Thank you.

vince

On Apr 14, 12:16 pm, Vincent A. [email protected] wrote:

Ah, yes. I knew it was something simple.
I’ll just sort countarray so that the highest index value is first, and
perhaps that will take care of the problem!
Thank you.

Another approach would be: instead of deleting each item, put nil at
each specified array index. Then, after you’re done iterating, delete
all the nils using Array#compact!, like so:

sizeList = [0,1,2,3,4,5,6]
countarray = [3,4,6]
countarray.each {|x| sizeList[x] = nil}
sizeList.compact!

On Tue, Apr 15, 2008 at 12:47:21AM +0900, Vincent A. wrote:

countarray = [3,4,6]

and the expected result should be 0,1,2,5, right?
What gives?

When you delete_at(3), your new array looks like:

sizeList[0] = 0
sizeList[1] = 1
sizeList[2] = 2
sizeList[3] = 4
sizeList[4] = 5
sizeList[5] = 6

. . . because sizeList[3] = 3.

When you then delete_at(4), your new array looks like:

sizeList[0] = 0
sizeList[1] = 1
sizeList[2] = 2
sizeList[3] = 4
sizeList[4] = 6

. . . because sizeList[4] = 5. Et cetera. Every time you delete an
element out of the middle, you shift everything to the right of it
leftward, basically.

You could try this:

sizeList = [0,1,2,3,4,5,6]
countarray = [6,4,3]
countarray.each {|x| sizeList.delete_at(x)}

That would start at the far end of the array and work backwards, so that
positional shifts after a deleted element won’t affect numbering for
stuff you want to delete later. If you can’t guarantee that the numbers
in countarray will be in reverse numerical order when you get them, you
can sort them that way thusly:

countarray = [3,6,4].sort.reverse

If you want to keep the array intact, rather than removing elements, and
just reset the values of specific elements to be “nothing”, you could
instead do something like this:

countarray.each {|x| sizeList[x] = nil}

There are probably more elegant ways to do what you want – and I’m not
entirely sure what you’re trying to accomplish anyway – but this might
help get you on the right track.

I’m not entirely sure what you’re trying to accomplish anyway

I guess he is starting to learn which could explain what he
is wanting to accomplish so examples don’t necessarily need to
make much sense and his head will slowly fill up with
101 ways to achieve what he wants to have :wink:

Marc H. wrote:

I’m not entirely sure what you’re trying to accomplish anyway

I guess he is starting to learn which could explain what he
is wanting to accomplish so examples don’t necessarily need to
make much sense and his head will slowly fill up with
101 ways to achieve what he wants to have :wink:

Basically, I’m trying to get a list of files/folders along with
a separate list of the sizes of said files. The array deletions
I asked about would be the deletion of files enclosed in folders
from the original file list and using the position of the items
in the file list to delete the corresponding array position in
the separate size list of those files.

And thanks to everyone who responded. 101 ways to do this would
be great, but I’ll settle for 4 or 5. :wink:

This has been very helpful.

vince

From: [email protected]

And thanks to everyone who responded. 101 ways to do this would

be great, but I’ll settle for 4 or 5. :wink:

i hope this catches up the 5th way :slight_smile:

ruby1.9 though,

irb(main):004:0> sizeList.group_by.with_index{|e,i| countarray.include?
i}
=> {false=>[0, 1, 2, 5], true=>[3, 4, 6]}

fr here you can get what gets deleted,

irb(main):016:0> sizeList.group_by.with_index{|e,i| countarray.include?
i}[true]
=> [3, 4, 6]

and what remains,

irb(main):017:0> sizeList.group_by.with_index{|e,i| countarray.include?
i}[false]
=> [0, 1, 2, 5]

the beauty here is you can go slowly, and picture out and compare in
advance what will be deleted and what will remain…

kind regards -botp

On 14.04.2008 19:25, Karl von Laudermann wrote:

sizeList = [0,1,2,3,4,5,6]
Btw, the conventional way in Ruby is to use “size_list” instead of
CamelCase for method and variable names.

countarray = [3,4,6]
countarray.each {|x| sizeList[x] = nil}
sizeList.compact!

Yet another alternative is to delete based on /criteria/ and not
/position/.

In this case that could be

note, the array in the block is not the original

array but contains entries to be deleted - it’s

only accidentally the same because of the way that

size_list is built.

size_list.delete_if {|x| [3,4,6].include? x}

Or any other criterium.

Kind regards

robert

From: Paul [mailto:[email protected]]

irb(main):001:0> a = [0,1,2,3,4,5,6]

=> [0, 1, 2, 3, 4, 5, 6]

irb(main):002:0> b = [3,4,6]

=> [3, 4, 6]

irb(main):003:0> c = a - b

=> [0, 1, 2, 5]

Hi Paul, the op meant that the array b contains indexes for a (and not
contents as in your example)

kind regards -botp

irb(main):001:0> a = [0,1,2,3,4,5,6]
=> [0, 1, 2, 3, 4, 5, 6]
irb(main):002:0> b = [3,4,6]
=> [3, 4, 6]
irb(main):003:0> c = a - b
=> [0, 1, 2, 5]
irb(main):004:0>

From: Peña, Botp [mailto:[email protected]]

irb(main):004:0> sizeList.group_by.with_index{|e,i|

countarray.include? i}

=> {false=>[0, 1, 2, 5], true=>[3, 4, 6]}

6th way :slight_smile:

:0> sizeList.values_at(*(0…sizeList.size-1).to_a-countarray)
=> [0, 1, 2, 5]

this one plays w the indices, then uses values_at to get back to the
contents.

kind regards -botp

On Tue, Apr 15, 2008 at 2:14 AM, Vincent A. [email protected]
wrote:

Basically, I’m trying to get a list of files/folders along with
a separate list of the sizes of said files. The array deletions
I asked about would be the deletion of files enclosed in folders
from the original file list and using the position of the items
in the file list to delete the corresponding array position in
the separate size list of those files.

Another solution could be, instead of having 2 arrays, to have
a single array where each position contains all the info for
a file, maybe like an array or hash, and then delete based on
the size info. Example:

file_info = [[“a.txt”, 123], [“b.txt”, 10000]]
file_info.delete_if {|x| x[1] > 1000}

will delete all entries whose size is bigger than 1000.

As a hash:

file_info = [{:name => “a.txt”, :size => 123}, {:name => “b.txt”,
:size => 10000}]
file_info.delete_if {|x| x[:size] > 1000}

or you could have a custom object if you need more complex modelling
of the file info.

Hope this helps,

Jesus.

On Apr 14, 5:47 pm, Vincent A. [email protected] wrote:

countarray = [3,4,6]

and the expected result should be 0,1,2,5, right?
What gives?

TIA,
vince

Mac OS X 10.5.2, G5 Quad, Textmate editor, ruby 1.86

What about this:

sizeList = [0,1,2,3,4,5,6]
countarray = [3,4,6]
countarray.sort.reverse.each {|x| sizeList.delete_at(x)}

Result: 0,1,2,5

sizeList = [0,1,2,3,4,5,6]
countarray = [3,4,6]
countarray.each {|x| sizeList.delete_at(x)}

Let’s look at what’s going on inside your .each block.
The first iteration it will delete the sizeList[3]. ===>
[0,1,2,4,5,6]
The second iteration it will delete sizeList[4] ===> [0,1,2,4,6]
The final iteration it will delete sizeList[6] ==> [0,1,2,4,6]
#there
is no sizeList[6] - deleteAt returns nil.

Your indexs are changing each time you call delete_at.

-Stephen

On Tue, Apr 15, 2008 at 9:15 AM, Robin P. [email protected]