jonepatr
jonepatr

Reputation: 7809

ruby looping question

I want to make a loop on a variable that can be altered inside of the loop.

first_var.sort.each do |first_id, first_value|
  second_var.sort.each do |second_id, second_value_value|
    difference = first_value - second_value
    if difference >= 0
      second_var.delete(second_id)
    else
      second_var[second_id] += first_value
      if second_var[second_id] == 0
        second_var.delete(second_id)
      end
      first_var.delete(first_id)
    end
  end
end

The idea behind this code is that I want to use it for calculating how much money a certain user is going to give some other user. Both of the variables contain hashes. The first_var is containing the users that will get money, and the second_var is containing the users that are going to pay. The loop is supposed to "fill up" a user that should get money, and when a user gets full, or a user is out of money, to just take it out of the loop, and continue filling up the rest of the users.

How do I do this, because this doesn't work?

Upvotes: 0

Views: 181

Answers (3)

philosodad
philosodad

Reputation: 1808

Okay. What it looks like you have is two hashes, hence the "id, value" split.

If you are looping through arrays and you want to use the index of the array, you would want to use Array.each_index.

If you are looping through an Array of objects, and 'id' and 'value' are attributes, you only need to call some arbitrary block variable, not two.

Lets assume these are two hashes, H1 and H2, of equal length, with common keys. You want to do the following: if H1[key]value is > than H2[key]:value, remove key from H2, else, sum H1:value to H2:value and put the result in H2[key].

H1.each_key do |k|
  if H1[k] > H2[k] then
     H2.delete(k)
  else 
     H2[k] = H2[k]+H1[k]
  end
end

Assume you are looping through two arrays, and you want to sort them by value, and then if the value in A1[x] is greater than the value in A2[x], remove A2[x]. Else, sum A1[x] with A2[x].

   b = a2.sort
   a1.sort.each_index do |k|
      if a1[k] > b[k]
        b[k] = nil
      else
        b[k] = a1[k] + b[k]
      end
    end
    a2 = b.compact

Based on the new info: you have a hash for payees and a hash for payers. Lets call them ees and ers just for convenience. The difficult part of this is that as you modify the ers hash, you might confuse the loop. One way to do this--poorly--is as follows.

e_keys = ees.keys
r_keys = ers.keys
e = 0
r = 0
until e == e_keys.length or r == r_keys.length
    ees[e_keys[e]] = ees[e_keys[e]] + ers[r_keys[r]]
    x = max_value - ees[e_keys[e]]
    ers[r_keys[r]] = x >= 0 ? 0 : x.abs 
    ees[e_keys[e]] = [ees[e_keys[e]], max_value].min
    if ers[r_keys[r]] == 0 then r+= 1 end
    if ees[e_keys[e]] == max_value then e+=1 end
end

The reason I say that this is not a great solution is that I think there is a more "ruby" way to do this, but I'm not sure what it is. This does avoid any problems that modifying the hash you are iterating through might cause, however.

Upvotes: 3

Roman A. Taycher
Roman A. Taycher

Reputation: 19505

Do you mean?

some_value = 5
arrarr = [[],[1,2,5],[5,3],[2,5,7],[5,6,2,5]]
arrarr.each do |a|
  a.delete(some_value)
end

arrarr now has the value [[], [1, 2], [3], [2, 7], [6, 2]]


I think you can sort of alter a variable inside such a loop but I would highly recommend against it. I'm guessing it's undefined behaviour. here is what happened when I tried it

a.each do |x| 
  p x
  a = []
end

prints 1 2 3 4 5 and a is [] at the end while

a.each do |x| 
  p x
  a = []
end

prints nothing and a is [] at the end

If you can I'd try using each/map/filter/select.ect. otherwise make a new array and looping through list a normally. Or loop over numbers from x to y

1.upto(5).each do |n| do_stuff_with(arr[n]) end

Upvotes: 1

Ryan Bigg
Ryan Bigg

Reputation: 107728

Assuming:

some_var = [1,2,3,4]

delete_if sounds like a viable candidate for this:

some_var.delete_if { |a| a == 1 }
p some_var
=> [2,3,4]

Upvotes: -1

Related Questions