user1934428
user1934428

Reputation: 22356

Delete several elements from an array

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

Answers (4)

Cary Swoveland
Cary Swoveland

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

Tom Lord
Tom Lord

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

spickermann
spickermann

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

Bhushan Kalode
Bhushan Kalode

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

Related Questions