Home > Mobile >  Each loop skipping an element when deleting an element within the each loop
Each loop skipping an element when deleting an element within the each loop

Time:12-21

I was just messing about trying to get all the 2, 3 and 4 combos in a size 4 array (e.g 1&2,1&3,1&4,2&3 and so on) and I came across this weird issue that I cannot figure out. Why is primary_num returning 4 on the 3rd loop but the index 0 value is 3, shouldn't it return 3.

The code is partial code but obviously can't complete it until I figure this part out. My next plan was to try a recursive function but was curious why this is happening when it works fine on loop 1 & 2. I know there is a permutations method but I am just experimenting.

lucky15 = [1,2,3,4]

doubles = []
triples = []
fourfold = []

lucky15.each_with_index do |primary_num, index|
    
    lucky15.delete(index)
    p lucky15[0]
    p primary_num
    p lucky15
end

The output for the puts was:

1 #p lucky15[0]
1 #p primary_num
[1, 2, 3, 4]

2 #p lucky15[0]
2 #p primary_num
[2, 3, 4]

3 #p lucky15[0]
4 #p primary_num <--- why 4 not 3????
[3, 4]

To explain what I was trying to do as my first revision:

lucky15 = [1,2,3,4]

doubles = []
triples = []
fourfold = []

lucky15.each_with_index do |primary_num, index|
  lucky15.delete(index)

  each.lucky15 do |num|
    doubles << "#{primary_num} && #{num}"
  end
end

puts doubles

CodePudding user response:

Looks like the delete method wasn't doing what you thought it was (same here, I had to run a modified version of this with more printing to see it):

lucky15 = [1,2,3,4]

doubles = []
triples = []
fourfold = []

lucky15.each_with_index do |primary_num, index|
    puts "Index: #{index}"   
    puts "0th element before delete: #{lucky15[0]}"
    lucky15.delete(index)
    puts "0th element after delete: #{lucky15[0]}"
    puts "primary_num: #{primary_num}"
    puts "lucky15: #{lucky15}"
end

Output:

Index: 0
0th element before delete: 1
0th element after delete: 1
primary_num: 1
lucky15: [1, 2, 3, 4]
Index: 1
0th element before delete: 1
0th element after delete: 2
primary_num: 2
lucky15: [2, 3, 4]
Index: 2
0th element before delete: 2
0th element after delete: 3
primary_num: 4
lucky15: [3, 4]

The index variable is holding the index each time, but delete is searching for the element in the array and deleting it if it's present. So when you use delete that way, you're deleting an element equal to the index from the array during each iteration, if such an element exists in the array during that iteration.

You probably want delete_at (https://ruby-doc.org/core-3.0.0/Array.html#method-i-delete_at).

CodePudding user response:

trying to get all the 2, 3 and 4 combos

To calculate combinations manually, you typically use nested loops. For "doubles", you could use:

lucky15 = [1, 2, 3, 4]
doubles = []

(0...lucky15.size-1).each do |i|
  (i 1...lucky15.size).each do |j|
    doubles << lucky15.values_at(i, j)
  end
end

doubles #=> [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

The outer loop increments an index i from 0 to 2 (because 2 is the maximum starting-index for a "2 combo"). The inner loop then increments another index j from i's successor up to 3 (the array's end index). This way, the inner loop "skips" the indices that have already been visited by i: (so there's no need to delete anything)

1 2 3 4  # array values
0 1 2 3  # array indices
i j      # lucky15.values_at(0, 1)

1 2 3 4
0 1 2 3
i   j    # lucky15.values_at(0, 2)

1 2 3 4
0 1 2 3
i     j  # lucky15.values_at(0, 3)

1 2 3 4
0 1 2 3
  i j    # lucky15.values_at(1, 2)

1 2 3 4
0 1 2 3
  i   j  # lucky15.values_at(1, 3)

1 2 3 4
0 1 2 3
    i j  # lucky15.values_at(2, 3)

To get "triples", you need another level:

triples = []

(0...lucky15.size-2).each do |i|
  (i 1...lucky15.size-1).each do |j|
    (j 1...lucky15.size).each do |k|
      triples << lucky15.values_at(i, j, k)
    end
  end
end

triples #=> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]

Visualized:

1 2 3 4
0 1 2 3
i j k

1 2 3 4
0 1 2 3
i j   k

1 2 3 4
0 1 2 3
i   j k

1 2 3 4
0 1 2 3
  i j k

However, Ruby already has the above methods built-in as combination so there's no need to re-invent the wheel:

doubles = lucky15.combination(2).to_a
#=> [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

triples = lucky15.combination(3).to_a
#=> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
  •  Tags:  
  • ruby
  • Related