Reputation: 33
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?
Upvotes: 1
Views: 71
Reputation: 27875
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.
Upvotes: 4
Reputation: 222368
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)
Upvotes: 0
Reputation: 19855
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
.
Upvotes: 2