Reputation: 22356
I came accross the following problem: Given an Array
named arr
of some arbitrary objects and an array indexes
holding some Fixnums between 0 and arr.size
. The task is to remove the elements from arr
at the index positions found in indexes
.
In my concrete example, the numbers in indexes
are all different, and sorted ascending, though I don't see how this property could help here.
I came up with the following implementations:
indexes.each { |i| arr.delete_at(i) }
or
arr.delete_if.with_index { |_,i| indexes.include?(i) }
While the first variant at least looks a bit elegant (compared to the second one), both seem to be not very efficient. Does by chance someone has an idea how to implement this in a more concise way?
Upvotes: 0
Views: 651
Reputation: 110755
I suggest doing that as follows.
arr = [1, 4, 6, 2, 7, 3]
indices_to_remove = [4, 0, 3]
arr.values_at((0..arr.size-1).to_a - indices_to_remove)
#=> [4, 6, 3]
This does not mutate arr
. To modify arr
in place write
arr.replace(arr.values_at((0..arr.size-1).to_a - indices_to_remove))
See Array#values_at, Array#- and Array#replace.
Upvotes: 1
Reputation: 28305
To avoid making multiple calls to Array#include?
for the indexes
, and also avoid mutating the original arr
, you could do:
arr.reject.with_index do |_, i|
next unless indexes.first == i
indexes.shift # !!
end
This will instead however mutate the indexes
object, so you'd need to account for that in the code.
Upvotes: 1
Reputation: 107107
I would iterate the indexes
in reverse order and just delete the records at the given index:
indexes.sort.reverse_each { |index| arr.delete_at(index) }
You need to use the reverse order because after removing an element from the array at position x all indexes after that position will change.
I choose to iterate only the indexes
array because that one is probably smaller than the arr
array which leads to fewer steps and is, therefore, most likely more performant than iterating the whole arr
array.
Upvotes: 3
Reputation: 54
Here is an alternate way to delete array element.
If you don't want to modify original array arr
arr.reject.with_index { |element, i| indexes.include? i }
If you want to modify original array arr
arr.reject!.with_index { |element, i| indexes.include? i }
Please refer example below
arr = [1, 2, 3, 4, 5]
indexes = [1, 3]
arr.reject.with_index { |element, i| indexes.include? i }
This will return [1, 3, 5] deleting 1st and 3rd element of array and array arr will be remains same as [1, 2, 3, 4, 5]
arr.reject!.with_index { |element, i| indexes.include? i }
This will return [1, 3, 5] deleting 1st and 3rd element of array again but array arr will be modify to [1, 3, 5]
Upvotes: 0