2

I have an array:

arr = [7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23]

and I want to modify my array by deleting at every step of -5, i.e., arr[-5], arr[-10], arr[-15] so the original array arrafter deletion equals:

arr = [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23]

What I've been told is its not good practice to delete from an array that you're iterating through. Are there any clean solutions?

2
  • delete_at accepts index. Give it an index. Commented Oct 11, 2013 at 11:17
  • 2
    Instead of mutating source array, build a copy of it, without those elements. Commented Oct 11, 2013 at 11:18

5 Answers 5

2

Just a modification of @tihom's answer, not relying on uniqueness, but using some ideas from "How to map with index in Ruby?":

>> arr.each_with_index.select{ |x, i| (arr.count-i)%5 != 0 }.map{ |x,i| x }
=> [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23]

Step by step it does:

  1. .each_with_index returns an Enumerator, which yields pairs of value and its index like [7, 0], [8, 1], [9, 2], ...
  2. .select{...} selects those pairs [x,i] for which count - i is not divisible by 5 (it would be all except count - 5, count - 10, etc)
  3. .map{ |x,i| x } transforms each pair to just its first element.

Not the most efficient, but at least clear, I think

BONUS:

I am sure that it is better to create a new array by some tranformation (in a functional-style) than modify it in-place, but if you insist on "in-place", here is a modification of @sawa's answer without hard-coded digits:

>> (-arr.count..-5).select {|i| i % 5 == 0}.each{|i| arr.delete_at(i)}
=> [-15, -10, -5]
>> arr
=> [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23]

Here we have a range from -size to -5, select only indices that are divisible by 5, and delete by them. As he said, it is important here to delete exactly in this order. But still to protect yourself from possible mistakes, I think it is safer yo never delete from an array you are iterating through and instead produce a new array (like in first method). Why complicate? ;)

Sign up to request clarification or add additional context in comments.

2 Comments

(-arr.count..-5).select {|i| i % 5 == 0} could be written as (-arr.count).step(-5, 5).
@undur_gongor, not exactly: if array has 16 elements, it would produce [-16, -11, -6], not [-15, -10, -5]. But if the OP can swear its size is divisible by 5, it's quite a good simplification. Thanks, didn't know about Numeric#step.
1

It is complicated to modify an array while iterating from the same side as the indices are counted. In other words, when you have positive indices, you should not iterate from the head to the tail while modifying; if you have negative indices, you should not iterate from the tail to the head. Otherwise, there is no problem.

Since you have negative indices, it is crucial that you modify from the head toward the tail.

[-15, -10, -5].each{|i| arr.delete_at(i)}
# => [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23]

1 Comment

@user2684075, see my answer for the development of sawa's idea (without hard-coded numbers)
1

Here is a simple solution:

p arr.reverse.each_slice(5).map{|x|x[0..-2]}.flatten.reverse
# => [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23]

2 Comments

I get a different result with the example input and your code: [7, 8, 9, 10, 13, 14, 15, 16, 19, 20, 21, 22]. And that's normal. How should the 7 get dropped with your approach?
Sorry, I edited my post without thinking, I think the best way is to the reverse the table first and reverse it back. Made an edit.
1

Based on the answer https://stackoverflow.com/a/19317883/908515:

arr.reverse.reject.with_index { | _, i | (i + 1) % 5 == 0 }.reverse
# i.e. arr.reverse.drop_every(5).reverse

If you want to modify the original array in-place, you can do

arr.reverse!.reject!.with_index { | _, i | (i + 1) % 5 == 0 }.reverse!

This is safe because the enumerator indices (i) are independent of the array indices.

Comments

1
arr.reject.with_index{|x,i| (i-arr.count) % 5 == 0 }
 => [8, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23] 

1 Comment

Still a good idea, I used it in my answer, which doesn't assume uniqueness.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.