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?
    elsif lowest.nil?
      if n>second_lowest
      elsif n<second_lowest
    elsif n<lowest
    elsif n>lowest && n<second_lowest

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

1 Answer 1


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

  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

  return lowest, second_lowest

If you want to test it

tests = [
  [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}"
  • \$\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\$ Feb 28, 2017 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, 2017 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\$ Mar 1, 2017 at 19:36
  • \$\begingroup\$ This strategy is called "early exit". It is discussed here. I hope it helps! \$\endgroup\$
    – Gabriel
    Mar 1, 2017 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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