简体   繁体   中英

SockMerchant Challenge Ruby Array#count not counting?

So, i'm doing a beginners challenge on HackerHank and, a strange behavior of ruby is boggling my mind.

The challenge is: find and count how many pairs there are in the array. (sock pairs)

Here's my code.

n = 100
ar = %w(50 49 38 49 78 36 25 96 10 67 78 58 98 8 53 1 4 7 29 6 59 93 74 3 67 47 12 85 84 40 81 85 89 70 33 66 6 9 13 67 75 42 24 73 49 28 25 5 86 53 10 44 45 35 47 11 81 10 47 16 49 79 52 89 100 36 6 57 96 18 23 71 11 99 95 12 78 19 16 64 23 77 7 19 11 5 81 43 14 27 11 63 57 62 3 56 50 9 13 45)

def sockMerchant(n, ar)
counter = 0
ar.each do |item|
  if ar.count(item) >= 2
    counter += ar.count(item)/2
    ar.delete(item)
  end
end
counter
end

print sockMerchant(n, ar)

The problem is, it doesn't count well. after running the function, in it's internal array ar still have countable pairs, and i prove it by running it again.

There's more. If you sort the array, it behaves differently.

it doesnt make sense to me.

you can check the behavior on this link

https://repl.it/repls/HuskyFrighteningNaturallanguage

You're deleting items from a collection while iterating over it - expect bad stuff to happen. In short, don't do that if you don't want to have such problems, see:

> arr = [1,2,1]
# => [1, 2, 1] 
> arr.each {|x| puts x; arr.delete(x) }
# 1
#  => [2]

We never get the 2 in our iteration.

A simple solution, that is a small variation of your code, could look as follows:

def sock_merchant(ar)
  ar.uniq.sum do |item|
    ar.count(item) / 2
  end
end

Which is basically finding all unique socks, and then counting pairs for each of them.

Note that its complexity is n^2 as for each unique element n of the array, you have to go through the whole array in order to find all elements that are equal to n .

An alternative, first group all socks, then check how many pairs of each type we have:

ar.group_by(&:itself).sum { |k,v| v.size / 2 }

As ar.group_by(&:itself) , short for ar.group_by { |x| x.itself } ar.group_by { |x| x.itself } will loop through the array and create a hash looking like this:

{"50"=>["50", "50"], "49"=>["49", "49", "49", "49"], "38"=>["38"], ...}

And by calling sum on it, we'll iterate over it, summing the number of found elements ( /2 ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM