3
\$\begingroup\$

Trying to find way to do this with only one pass through the array, so I won't run a sort on it and take arr[0..1]. Not sure how to make it look better:

def LowestSecondLowest(arr)
  lowest,second_lowest = nil,nil

  arr.each do |n|

    if second_lowest.nil?
      second_lowest=n
    elsif lowest.nil?
      if n>second_lowest
        lowest=second_lowest
        second_lowest=n
      elsif n<second_lowest
        lowest=n
      end
    elsif n<lowest
      second_lowest=lowest
      lowest=n
    elsif n>lowest && n<second_lowest
      second_lowest=n
    end       
  end

  "#{lowest} #{second_lowest}"
end
\$\endgroup\$
2
  • \$\begingroup\$ It often results in simpler code to start with lowest, second_lowest = VERY_LARGE_NUMBER, VERY_LARGE_NUMBER \$\endgroup\$ – Marc Rohloff Feb 27 '17 at 22:08
  • 1
    \$\begingroup\$ Array#min can return the two lowest values: [3,2,1].min(2) \$\endgroup\$ – Maxim Krizhanovsky Mar 21 '17 at 12:10
3
\$\begingroup\$

Because Array lets you put repeated elements of same value, I believe that the two lowest elements in [1, 2, 1] are [1, 1], not [1, 2]. This is the very same behavior one would face if sorting the array and getting the two first elements. Also I believe that in [1] there is a lowest and no 2nd lowest (also, the very same behavior if sorting and getting the first two).

With this in mind, I changed your code to obey these premises (and make it a bit more readable if I may say). Take a look.

def two_lowest arr
  # if arr has no elements, there is no answer
  # If arr has only one element, this is the lowest
  if arr.size < 2 then
    return arr.first, nil
  end

  lowest, second_lowest = nil, nil

  arr.each do |n|
    if lowest.nil? or n < lowest
      # if we have no lowest or we found an element lower than current lowest,
      # update our lowest and 2nd lowest
      second_lowest = lowest
      lowest = n
    elsif second_lowest.nil? or n < second_lowest
      # if we have no 2nd lowest or we found an element between lowest and 2nd
      # lowest, update our 2nd lowest
      second_lowest = n
    end       
  end

  return lowest, second_lowest
end

If you want to test it

tests = [
  [],
  [1],
  [1, 1],
  [1 ,2],
  [1, 2, 1],
  [1, 2, 3],
  [1, 1, 2, 3],
  [3, 2, 1, 1],
]

for test in tests
  puts "In #{test}:"
  puts "#{two_lowest test}"
  puts
end
\$\endgroup\$
4
  • \$\begingroup\$ Thanks very much, Gabriel. I understand the logic behind your revisions. I think my sloppiness came from not considering what would get caught in previous if/elsif statements, and therefore trying to accommodate unnecessary values in subsequent if/elsif statements \$\endgroup\$ – Matthew Farabaugh Feb 28 '17 at 14:14
  • \$\begingroup\$ Treating corner cases before main iteration/recursion is a "trick" I usually do to simplify my code. So after all ifs I can forget about corner cases and write just for the general case. I am glad I could help you, it is my first answer in CodeReview :-) \$\endgroup\$ – Gabriel Feb 28 '17 at 17:28
  • \$\begingroup\$ Would you mind elaborating for me what you mean? I'm still rather new to coding and general strategies like you mention here are not familiar to me. Thanks! \$\endgroup\$ – Matthew Farabaugh Mar 1 '17 at 19:36
  • \$\begingroup\$ This strategy is called "early exit". It is discussed here. I hope it helps! \$\endgroup\$ – Gabriel Mar 1 '17 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.