Ekkstein
Ekkstein

Reputation: 819

Remove array elements at indices non destructively

Let's say you have something like this:

my_array = ['some_rather', 'long_named', 'array_element', 'entry']

I want to remove arbitrary entries by index from my_array without changing it and I want the filtered (i.e. array with indices removed) to be returned from my call. Furthermore, I want to avoid chaining 4 separate calls and write a block doing so.

Example:

filtered_array = my_array.drop_indices(1,3)

Upvotes: 1

Views: 271

Answers (3)

Oleksandr Holubenko
Oleksandr Holubenko

Reputation: 4440

One more possible solution with some addition, now it's also will work with negative indexes:

array = %w( a b c d e f )
indexes = [1, 2, -1, -9, -6, 6]

def array_except(array, *indexes)
  indexes = indexes.map { |e| e.negative? ? e + array.length : e }
  array.values_at(*((0...array.length).to_a - indexes))
end

array_except(array, *indexes)
=> ["d", "e"]
array_except(array, 0, -1)
=> ["b", "c", "d", "e"]

Upvotes: 0

Ekkstein
Ekkstein

Reputation: 819

For Arrays with duplicate Elements the best I know is:

array = [0,15,8,15,8]
indices_to_remove = [1,4]
res = array.reject.with_index{ |_,i| indices_to_remove.include?(i) }

returns

[0,8,15]

Additionally for arrays with unique entries such as a set of users

(using variable definitions from the question)

filtered_array = my_array - my_array.values_at(1,3)

Bonus, if your indices are inside an array themselves:

indices_to_remove = [1,3]
filtered_array = my_array - my_array.values_at(*indices_to_remove)

I think this is rather descriptive, not awkward and not shorter than needed.

Upvotes: 0

Simple Lime
Simple Lime

Reputation: 11090

You could chain Enumerable's with_index onto Array's reject method to do what you want, though this might violate your desire to not chain separate method calls or write a block to do this:

my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]

filtered = my_array.reject.with_index { |_, index| indices_to_remove.include?(index) }

p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]

If this isn't acceptable, the only other thing I can think of right now, to keep duplicate items (as noted in my comment to your solution), is to change from indices_to_remove to indices_to_keep:

my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]
indices_to_keep = [*(0...my_array.length)] - indices_to_remove

filtered = my_array.values_at(*indices_to_keep)
p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]

Upvotes: 2

Related Questions