1

I am trying to get a list of colors to print after the color blue without using the index. I did this

colors = ["Red", "Blue", "Green", "Purple", "White", "Black"]

colors.each { |item| print item if item > "Blue" }

but the output is this

Red Green Purple White

Does anyone know why?

3
  • 3
    Because you are printing all items that is alphabetically after blue Commented Nov 26, 2016 at 15:26
  • Why not use index? Commented Nov 26, 2016 at 15:44
  • thanks. davidhu2000 Commented Nov 26, 2016 at 16:11

3 Answers 3

4

The question "Why" is already answered in the comment of davidhu2000:

Because you are printing all items that is alphabetically after blue

If you don't want to use the index, then you can use a solution with a help variable:

colors = ["Red", "Blue", "Green", "Purple", "White", "Black"]

blue_found  = false
colors.each { |item| 
  if blue_found
    print item
  else
      blue_found ||= item == "Blue" 
  end
}

If you prefer a one-liner you could use

  blue_found ? (print item) :  (blue_found ||= item == "Blue" )

Another possibility:

colors = ["Red", "Blue", "Green", "Purple", "White", "Black"]

blue_found  = false
colors.each { |item| 
  print item if blue_found 
  blue_found ||= item == "Blue" 
}

I was curious, why you don't want to use the index, so I made some more research outside your question.

The solution with index would be:

colors.each_with_index { |item,i| 
  print item if i > colors.index("Blue")
}

The index-method may need some more runtime. To test it, I made a little benchmark:

colors = ["Red", "Blue", "Green", "Purple", "White", "Black"]
require 'benchmark'

TEST_LOOPS = 100_000

def action(item)
  #Just a no-action.
end

Benchmark.bm(10) {|b|

  b.report('test/variable') {
   TEST_LOOPS.times { 
    blue_found  = false
    colors.each { |item| 
      action(item) if blue_found 
      blue_found ||= item == "Blue" 
    }
   }            #Testloops
  }             #b.report

  b.report('test/index') {
   TEST_LOOPS.times { 
    colors.each_with_index { |item,i| 
      action(item) if i > colors.index("Blue")
    }
   }            #Testloops
  }             #b.report

  b.report('test/index2') {
   TEST_LOOPS.times { 
    index_blue = colors.index("Blue")
    colors.each_with_index { |item,i| 
      action(item) if i > index_blue
    }
   }            #Testloops
  }             #b.report

  b.report('test/dogbert') {
   TEST_LOOPS.times { 
      # Drop all items until you find "Blue", then drop the "Blue".
      colors.drop_while { |item| item != "Blue" }.drop(1).each do |item|
        action(item)
      end
    }            #Testloops
  }             #b.report

  b.report('test/pjs') {
   TEST_LOOPS.times { 
      after_blue = colors.dup
      loop do
        break if after_blue.shift == 'Blue'|| after_blue.length < 1
      end
      after_blue.each{|item| action(item) }
   }            #Testloops
  }             #b.report

} #Benchmark

The result:

                 user     system      total        real
test/variable  0.187000   0.000000   0.187000 (  0.179010)
test/index     0.250000   0.000000   0.250000 (  0.254015)
test/index2    0.140000   0.000000   0.140000 (  0.136008)
test/dogbert   0.110000   0.000000   0.110000 (  0.111006)
test/pjs       0.327000   0.000000   0.327000 (  0.332019)

So the version with a help variable is (as expected) faster. But if you define the index once outside the loop, then the solution with index (test/index2) is nearly as fast as test/variable.

Disclaimer: I made not many thoughts for faster solutions. So maybe there is a more efficient way. Thanks to dogbert for his hint.

Sign up to request clarification or add additional context in comments.

4 Comments

The "without index" requirement is likely from exercise, not from performance bottleneck.
Thanks. Your answer is very resourceful.
You'd get a huge speedup if you move the Array#index call outside the .each_with_index block. Right now that implementation is O(n^2), which would be O(n) if you move that out. (But it's still slower than the first implementation since it has to traverse the array twice.)
@Dogbert Thanks. I added your proposal to the benchmark.
2

Another variant that runs neck and neck with @knut's test/variable benchmark is:

def values_after(target, arr)
  result = arr.dup
  loop do
    break if result.shift == target || result.length < 1
  end
  result
end

colors = ['Red', 'Blue', 'Green', 'Purple', 'White', 'Black']
puts *values_after('Blue', colors)
# Or, if you prefer all on one comma-separated line:
#   puts values_after('Blue', colors).join(', ')

The || result.length < 1 is a guard clause to prevent an infinite loop if target is not an element in arr.

4 Comments

I like the idea with the array duplication. In case of a re-usage you wouldn't need the checks again. I didn't get the reason for ``. Can you explain why you need it? At least you still need a after_blue.each{|item| print item } to give the result.
Looks like a garbled transmission, not sure what you're asking since I don't have any backticks. When you're done, the after_blue array contains only those elements after the "Blue" entry, and you can do whatever you want with them, however you want.
Sorry, yes some parts get lost. I didn't get the reason for || after_blue.length < 1
@knut Rewrote it as a method, and added the requested explanation as part of the answer.
0

Although the two answers already present give the correct answer, I'd use a more functional style, which in my opinion is much more readable.

colors = ["Red", "Blue", "Green", "Purple", "White", "Black"]
# Drop all items until you find "Blue", then drop the "Blue".
colors.drop_while { |item| item != "Blue" }.drop(1).each do |item|
  puts item
end

Since @knut mentioned benchmarks, this code is only a tiny bit slower than @knut's test/variable benchmark:

                 user     system      total        real
test/variable  0.790000   0.000000   0.790000 (  0.790960)
test/drop      0.900000   0.000000   0.900000 (  0.898137)

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.