Best way to switch values - ruby

I would like to find the most effective and short way to do the following:
if current_value == "hide"
current_value = "show"
elsif current_value == "show"
current_value = "hide"
end
So, i would like to set the opposite to what the current situation is.
Thanks!

What about ternary?
current_value == "hide" ? current_value = "show" : current_value = "hide"
Maybe this would be better :
current_value = (current_value == "hide") ? "show" : "hide"

Four ways:
#1
c = 'show'
c = c['hide'] ? 'show' : 'hide'
#2
c = case c
when 'hide' then 'show'
else 'show'
end
#3
c, = ['show','hide']-[c]
#4
show = ['show', 'hide'].cycle
p show.next #=> 'show'
p show.next #=> 'hide'
p show.next #=> 'show'

One way that is not too pretty but will work:
current_value = (["hide", "show"] - [current_value])[0]

If current_value can only be "show" or "hide", why don't you use a boolean variable, say is_visible?
Then just toggle it like this:
is_visible = !is_visible

How about this?
VALUES = {
'show' => 'hide',
'hide' => 'show',
}
current_value = VALUES[current_value]
Another unorthodox approach :)
VALUES = %w[hide show hide]
current_value = 'show'
current_value = VALUES[VALUES.index(current_value) + 1] # => "hide"

I think the right way to go is to keep a boolean value like Yanhao suggests, and if you are to call that in a CSS class, use a ternary there.
current_value = true # initial setting
...
current_value ^= true # alternating between `true` and `false`
...
current_value ? "hide" : "show" # using it to call a string value

A way that can be reused more easily (edit toggle values more easily)
c = 'show'
TOGGLE_VALUES = ['show', 'hide']
c = TOGGLE_VALUES[ TOGGLE_VALUES.index(c) - 1]

When one is late to the party (this is answer #8), one must dig deep. The best solution does not often result, but the grey cells do get some exercise. Here are a few more ways to flip 'show' and 'hide' (in no particular order):
SH = 'showhide'
a = ['show', 'hide']
h = {'show'=>'hide'}
c = 'show'
1
c = SH.sub(c,'') #=> 'hide'
2
c = SH[/.+(?=#{c})|(?<=#{c}).+/] #=> 'hide'
3
c = (SH.end_with? c) ? "show" : "hide" #=> 'hide'
4
d = "hide"
c, = (c,d = d,c) #=> 'hide'
5
c = SH.chomp(c).reverse.chomp(c.reverse).reverse #=> 'hide'
6
c, = a.reverse! #=> 'hide'
7
c = (h=h.invert)[c] #=> 'hide'
In #2, (?=#{c}) and (?<=#{c}) are positive lookahead and positive lookbehind, respectively.

Related

Multiple results in ? style if clause

I have the following code: i ? "x" : "y" But instead of only returning either "x" or "y" I also want to set i either true or false. i ? ("x"; i = false) : ("y"; i = true) however does not work.
(i ? "x" : "y").tap{i = !i}
or
(i = !i) ? "y" : "x"
But if this turns out to be an XY-situation (I don't write "XY-question" here because the OP has not asked any question), then this might be more elegant:
letter = ["x", "y"].cycle
letter.next #=> "x"
letter.next #=> "y"
letter.next #=> "x"
letter.next #=> "y"
...
If you want to return 'x' or y', then 'x' or 'y' needs to be the last statement:
i ? (i = false; 'x') : (i = true; 'y')
If you think of it like this, maybe it would make more sense:
if i
i = false
'x'
else
i = true
'y'
end
Keep in mind that setters in Ruby (and many other languages) return the value being set. For example, i = false returns false.
You should use ternary operator only in the simplest cases because readability and side-effect issues.
Ruby Code Style
But if you really want it you could do something like this:
!(i = !i) ? 'x' : 'y'

In Ruby, how to conditionally add several members to an object?

I would like to create a hash with several members added conditionally.
The simple approach is:
var a = {}
a['b'] = 5 if condition_b
a['c'] = 5 if condition_c
a['d'] = 5 if condition_d
Now, I would like to write a more idiomatic code. I am trying:
a = {
b => (condition_b? 5 : null),
c => (condition_c? 5 : null),
d => (condition_d? 5 : null)
}
But now, a.length equals 3 whatever conditions are met. This is not the desired result.
Is there a handy solution?
May be not exactly what you want but this can help
array = ['b','c','d'] ##array of values you want as keys
a ={}
array.each do |val|
a[val] = 5 if send("condition_"+val.to_s) #call method condition_#{key_name}
end
If the conditions are not related you can use your own hash and you can
a = {
b => (condition_b? 5 : nil),
c => (condition_c? 5 : nil),
d => (condition_d? 5 : nil)
}
a.values.compact.size
to get length of values other then nil
How about you only add to the hash if the condition is met, like this:
a = {}
a.merge!({'b'=>5}) if condition_b
a.merge!({'c'=>5}) if condition_c
In the second way, you're always going to have the three keys; the conditions only determine the particular values. So if you want to use that syntax, you will need to define a custom method that only counts the keys if they are not nil (also, it's nil in Ruby, not null). Something like:
def non_nil_length(hash)
i = 0
hash.each_pair do |k,v|
if !v.nil?
i += 1
end
end
i
end
There's probably a better way to do that, but there's the quick and dirty.

How to check that only one value in an array is non-zero?

Given an array like : [0,1,1]
How can I elegantly check that: Only one element has a non-zero value and that the others are 0?
(So the above array will fail the check while this array will pass : [1,0,0])
my_array.count(0) == my_array.length-1
If speed is important, for very large arrays where you might need to return early upon detecting a second non-zero, perhaps:
def only_one_non_zero?( array )
found_non_zero = false
array.each do |val|
if val!=0
return false if found_non_zero
found_non_zero = true
end
end
found_non_zero
end
Select at most two non-zero elements, and check if exactly one item was available.
>> [0,1,1].select {|x| !x.zero?}.take(2).size == 1
=> false
>> [0,1,0].select {|x| !x.zero?}.take(2).size == 1
=> true
>> [1,2,3].select {|x| !x.zero?}.take(2).size == 1
=> false
Works fine in Ruby 1.8.7, but note that select returns an array, so it's not "optimally lazy". Here's a blog post showing how to make some lazy enumerators in Ruby.
Thanks for all your answers!
I solved too:
input_array = [0,0,0]
result = input_array - [0]
p result.size == 1 && result[0] == 1
Ruby, I love you!

Ruby: Best way to convert a String to Integer or leave it as String if necessary?

Developing a little survey webapp, ran into problem that deals with ranges for rating type questions.
So a rating's range could be:
1..10
-5..0
-5..5
'a'..'z'
'E'..'M'
and so on
The range is stored as a pair of varchars in database (start and end of range). So range always starts off as a string input.
What is the best way to take these string values and build a Ruby Range accordingly.
I can't just go value.to_i as this won't work for string iteration. Having a bunch of if's seems ugly. Any better way?
Not as important, but worth asking:
Also what if I wanted to make it all work with reversed range? Say 5-to-0 or G-to-A. I know that Ruby doesn't support reverse range (since it uses succ() to iterate). What would be the best way here?
Thanks in advance!
Update:
Based on Wouter de Bie's suggestion I've settled for this:
def to_int_or_string(str)
return str.match(/^-?\d+$/) ? str.to_i : str.strip
end
def ratings_array(from, to)
from = to_int_or_string(from)
to = to_int_or_string(to)
from > to ? Range.new(to, from).to_a.reverse : Range.new(from, to).to_a
end
Any thoughts?
Use Range.new:
Range.new("a","z")
=> "a".."z"
Range.new(-5,5)
=> -5..5
If you're varchars contain quotes, you can use eval to get the right ranges:
from = "'a'"
to = "'z'"
eval("Range.new(#{from},#{to})")
Otherwise you could use value.to_i to figure out if it was a number or a string in the varchar:
a = "x"
a = (a.to_i == 0 && a != "0") ? a : a.to_i
=> "x"
a = "5"
a = (a.to_i == 0 && a != "0") ? a : a.to_i
=> 5
Which of course can be nicely extracted into a method:
def to_int_or_string(value)
return (value.to_i == 0 && value != "0") ? value : value.to_i
end
def to_range(from, to)
return Range.new(to_int_or_string(from), to_int_or_string(to))
end
To reverse your range, you have to convert it to an array first:
Range.new("a","g").to_a.reverse
=> ["g", "f", "e", "d", "c", "b", "a"]
You can do something like the following.
str = 'Z..M'
v1 = str[0,str.index('.')]
v2 = str[str.index('.')+2, str.length]
unless v1.to_i == 0
v1 = v1.to_i
v2 = v2.to_i
end
if v2>v1
final_arr = (v1..v2).to_a
else
final_arr = (v2..v1).to_a.reverse
end
puts final_arr
This takes care of both the positive and the negative ranges

dropdown select box with "filter as you type" in shoes

Question: Does anyone have an example of a "filter as you type" dropdown control using Shoes?
Examples: If you are looking for examples of what i am talking about, see these.
http://docs.jquery.com/Plugins/Autocomplete
Jquery: Filter dropdown list as you type
I've never seen one in the wild. Here's the code for one I started working on a while back before getting distracted. It's extremely rough, but maybe you can take it from here:
class Dropdown < Widget
def initialize (list, opts = {})
#max_items = opts[:max_items] || 5
#min_letters = opts[:min_letters] || 3
#width = opts[:width] || 280
#fill = opts[:background] || white
#highlight = opts[:highlight] || yellow
#match_anywhere = opts[:match_anywhere].nil? ? true : opts[:match_anywhere]
#ignore_case = opts[:ignore_case].nil? ? true : opts[:ignore_case]
#entries = list
#text_box = edit_line :width => #width do |box|
if box.text.length >= #min_letters
update_list(box.text)
else
#list.clear if #list
#list = nil
end
end
end
def update_list(search)
search.downcase! if #ignore_case
choices = []
#entries.collect do |x|
temp = #ignore_case ? x.downcase : x
if #match_anywhere
choices << x if temp.include?(search)
else
choices << x if temp.index(search) == 0
end
break if choices.length == #max_items
end
#list.clear if #list
#list = nil
app.append do
#list = stack :width => #width do
background #fill
choices.each do |choice|
f = flow { para choice }
f.click do
#text_box.text = choice
#list.clear if #list
#list = nil
end
f.hover {|h| h.contents.first.fill = #highlight }
f.leave {|l| l.contents.first.fill = nil }
end
end
end unless choices.nil?
#list.move(0,0)
#list.displace(0, #text_box.height + #text_box.top)
end
end
Shoes.app(:width => 500, :height => 500) do
dropdown ['Breed', 'Green', 'Greetings', 'Phoning', 'Ponies', 'Reed', 'Trees'], {:max_items => 3}
para 'Ponies are awesome!'
para 'Bananas are yellow.'
para 'Sometimes I like to eat bananas, but never ponies.'
end
Try the "jquery options filter", based on real select box and matching in the mid of option texts:
http://plugins.jquery.com/project/jquery_options_filter
for diyism

Resources