views:

133

answers:

5
+1  Q: 

Ruby Nested Loop

Hi, I have a nested loop2(check2) in loop1(check1) but it seems that nested loop2(check2) only runs once.

Both loops contain the same array. This script is used to check for a duplicate id in check1.

check1=["0", "0", "0", "1", "1", "2", "3", "4", "5", "100", "4294967294", "9", "11", "6", "200", "7", "201", "811", "202", "204", "3000", "205", "3001", "3001", "3001"]
check2 =["0", "0", "0", "1", "1", "2", "3", "4", "5", "100", "4294967294", "9", "11", "6", "200", "7", "201", "811", "202", "204", "3000", "205", "3001", "3001", "3001"]

Code:

check1.each do |check1|
  counter=0
  puts "checking against:"+check1.to_s
  check2.each do |check2|
    puts "checking:"+check1.to_s+"|"+check2.to_s
    if check1 == check2
      counter += 1
    end
  end
  if counter > 1
    dupUID << check1
  end
end

The result:

checking against:0 <- checking the 1st element in check1
checking:0|0
checking:0|0
checking:0|0
checking:0|1
checking:0|1
checking:0|2
checking:0|3
checking:0|4
checking:0|5
checking:0|100
checking:0|4294967294
checking:0|9
checking:0|11
checking:0|6
checking:0|200
checking:0|7
checking:0|201
checking:0|811
checking:0|202
checking:0|204
checking:0|3000
checking:0|205
checking:0|3001
checking:0|3001
checking:0|3001
checking against:0<- checking the 2nd element in check1
checking:0|3001   <- nested loop2(check2) is not looping again on the 2nd element of loop 1
checking against:0
checking:0|3001   <- loop2 stops at the last element for the remaining elements in check1
checking against:1
checking:1|3001
checking against:1
checking:1|3001
checking against:2
checking:2|3001
checking against:3
checking:3|3001
checking against:4
checking:4|3001
checking against:5
checking:5|3001
checking against:100
checking:100|3001
checking against:4294967294
checking:4294967294|3001
checking against:9
checking:9|3001
checking against:11
checking:11|3001
checking against:6
checking:6|3001
checking against:200
checking:200|3001
checking against:7
checking:7|3001
checking against:201
checking:201|3001
checking against:811
checking:811|3001
checking against:202
checking:202|3001
checking against:204
checking:204|3001
checking against:3000
checking:3000|3001
checking against:205
checking:205|3001
checking against:3001
checking:3001|3001
checking against:3001
checking:3001|3001
checking against:3001
checking:3001|3001

Can anyone point out my mistake? Thanks a lot. Solved: Thanks to all!

check1.each do |ch1|
  counter=0
  check2.each do |ch2|
    if ch1 == ch2
      counter += 1
    end
  end
  if counter > 1
    dupUID << ch1
  end
end

puts dupUID
+2  A: 

I guess you shouldn't write such code. There is a better solution:

x = [0, 10, 15]
y = [0, 20, 15]
x & y # => [0, 15] 

This method returns elements common to the specified two arrays.

(Updated) There is another way to do it within only one array:

[0, 10, 10, 15, 20].inject({}) 
{ 
    |a, c| a[c] ||= 0; a[c] = a[c].next; a 
}.delete_if { |k, v| v == 1 }.keys
floatless
Hi, sorry for the misunderstanding, i need to find the duplicate id in check1. meaning 0 ,1 ,3001 should be flagged out in dupUID
cherhui
+2  A: 

You are shadowing the check1 and check2 arrays because the do block variables have the same name as them.

After the inner do block, check2 refers to the last element of the array, not the array itself.

To fix this, rename the block variables to something like ch1 and ch2.

So this explains why the nested loop isn't running as you expect. Actually, your algorithm itself is also flawed. The answer of @floatless gives a better approach to the problem.

thanks! what a careless mistake i've made
cherhui
+1  A: 

Shorter, but not more understandable:

check.inject(Hash.new(0)) { |a, x| a[x] += 1; a }.reject { |k, v| v <= 1 }.keys
Amadan
A: 

Assuming you have a single array check and you want to find all duplicate elements within it. Using Ruby 1.9.

check.group_by {|v| v}.map { |k, v| v.size > 1 ? k : nil }.compact

Explanation:

  • group_by returns a Hash with key being the number and value being an array of each occurrence.
  • map returns either nil if the value occurs only once, or the value if it occurs more than once.
  • compact cleans up all nil values.

Here are the step by step results:

# after group_by
{"204"=>["204"], "6"=>["6"], "11"=>["11"], "205"=>["205"], "7"=>["7"], "811"=>["811"], "9"=>["9"], "4294967294"=>["4294967294"], "0"=>["0", "0", "0"], "100"=>["100"], "1"=>["1", "1"], "200"=>["200"], "2"=>["2"], "201"=>["201"], "3"=>["3"], "3000"=>["3000"], "202"=>["202"], "4"=>["4"], "3001"=>["3001", "3001", "3001"], "5"=>["5"]}>

# after map
[nil, nil, nil, nil, nil, nil, nil, nil, "0", nil, "1", nil, nil, nil, nil, nil, nil, nil, "3001", nil]

# after compact
["0", "1", "3001"]
Anurag
A: 

If you had warnings on ($VERBOSE = true), it'd notify you about the mistake you had.

IRB for ruby 1.9.1 doesn't allow you to turn on $VERBOSE at the command line, but 1.9.2 will.

Update: This question lead me to file this bug/feature improvement for ruby. Thanks!

Andrew Grimm