LuminaChen
LuminaChen

Reputation: 135

Conditionally inserting data into an array

Alright, can someone help me how to properly iterate a dynamic size array? Here's what I mean:

my_array = [1, 2, 3, 4, 2, 5, 15, 2] # <= there are three 2 inside my_array 

Then I would like to add "good" everytime the iteration hit integer 2, I tried several method but not find the way, here's the best method I've tried(still not resulting in what I want)

# First Method
for i in 0..(my_array.length - 1)
 my_array.insert(i + 1, "good") if my_array[i] == 2
end

p my_array # => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2] 

# Second Method
for i in 0..(my_array.length - 1)
 my_array[i + 1] = "good" if my_array[i] == 2
end

p my_array # => [1, 2, "good", 4, 2, "good", 15, 2, "good"] 

The first method is not good because it's not showing "good" after the last 2, I guess this because the iteration could not reach the last integer 2(in the last array) and that is expected because the array size is changed bigger everytime "good" is inserted.

The second one is also bad, because I replace the data after every 2 with "good" string. Now can someone point it out to me how can I doing this properly so I can produce it like this:

 p my_array # => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]

All "good" is added without replacing any data.

Any help is appreciated, thank you very much.

Upvotes: 0

Views: 1890

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110675

Here's another way you can do it, using an Enumerator:

my_array = [1, 2, 3, 4, 2, 5, 15, 2]

enum = my_array.each
  #=> #<Enumerator: [1, 2, 3, 4, 2, 5, 15, 2]:each>

my_array = []
loop do
  x = enum.next
  my_array << x
  my_array << "good" if x == 2
end
my_array
  #=> [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]

Enumerator#next raises a StopInteration exception when the enumerator is already on the last element. Kernel#loop handles the exception by breaking the loop. That's why you will often see loop used when stepping through an enumerator.

Upvotes: 0

tadman
tadman

Reputation: 211570

You'd have a better time transforming this into a new array than modifying in-place:

my_array = [1, 2, 3, 4, 2, 5, 15, 2]

def add_good(a)
  a.flat_map do |value|
    case (value)
    when 2
      [ 2, 'good' ]
    else
      value
    end
  end
end

puts add_good(my_array).inspect
# => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]

The flat_map method is useful for situations where you want to create zero or more entries in the resulting array. map is a 1:1 mapping, flat_map is a 1:N.

It's also possible to make this much more generic:

def fancy_insert(a, insert)
  a.flat_map do |value|
    if (yield(value))
      [ value, insert ]
    else
      value
    end
  end
end

result = fancy_insert(my_array, 'good') do |value|
  value == 2
end

puts result.inspect

That way you can pass in an arbitrary block and value to be inserted.

Upvotes: 3

Jorge de los Santos
Jorge de los Santos

Reputation: 4633

Why not using the .each_with_index method:

arr = [1, 2, 3, 4, 5, 2, 3, 5]
arr.each_with_index {|e, i| arr.insert(i+1, "good") if e == 2}

Fast and furious.

Upvotes: 2

Related Questions