Is there a way to conditionally add to an array in one line?
I have str1 and str2. str1 may or not be an empty string, and I want to construct an array like:
str1 = ""
str2 = "bar"
["bar"]
or
str1 = "foo"
str2 = "bar"
["foo", "bar"]
I can only figure out a way to do this on tw开发者_JAVA技巧o lines right now but I know there must be a way to do it one.
In ruby 1.9
[*(str1 unless str1.empty?), str2]
In ruby 1.8
[(str1 unless str1.empty?), str2].compact
[str1, str2].reject {|x| x==''}
# ...or...
[str1, str2].reject &:empty?
Object#tap
[:starting_element].tap do |a|
a << true if true
a << false if false
a << :for_sure
end
# => [:starting_element, true, :for_sure]
So in one line
[].tap { |a| [foo, bar].each { |thing| a << thing unless thing.blank? } }
[bar].tap { |a| a << bar unless foo.blank? }
You can use delete_if:
['', 'hola'].delete_if(&:empty?)
If you're using Rails, you can replace empty? by blank?
['', 'hola'].delete_if(&:blank?)
or use a block:
['', 'hola'].delete_if{ |x| x == '' }
Note that sawa's proposed answer for Ruby 1.9 (currently the answer with the most votes) has issues when used with a hash - as follows;
> [*({foo: 1} if true), {foo: 2}]
[
[0] [
[0] :foo,
[1] 1
],
[1] {
:foo => 2
}
]
Note that the compact example works as you would expect;
[({foo: 1} if true), {foo: 2}].compact
[
[0] {
:foo => 1
},
[1] {
:foo => 2
}
]
You can use a ternary statement:
ary = (str1.empty?) ? [ str2 ] : [ str1, str2 ]
str1 = ''; str2 = 'bar'
(str1.empty?) ? [ str2 ] : [ str1, str2 ] #=> ["bar"]
str1 = 'foo'; str2 = 'bar'
(str1.empty?) ? [ str2 ] : [ str1, str2 ] #=> ["foo", "bar"]
Another way,
(str1.present? ? str1 : []) + [str2]
Perhaps a more concise version of Cyril's answer:
Array.new.tap do |array|
if condition
array << "foo"
end
end
You could monkey-patch Aray pr Enumerable and provide a conditional adding method.
module Array
def add_if(object, condition=true)
self << object if condition
return self
end
end
That way, it would be chainable, preserving most space in line.
array = [].add(:class, is_given?).add(object, false) #etc
A more compact way to do this is using presence
, which returns the object if present, or nil
if not.
nil
values can then be stripped with .compact
:
[str1.presence, str2].compact
or even better, the magical *
splat operator:
[*str1.presence, str2]
(And yes, pun was intended)
my_array = [str1, str2].find_all{|item| item != ""}
In a way, I feel like there are 2 different questions being asked:
- Is there a way to conditionally add to an array in one line?
- How do I achieve this specific task.
Regarding question #1:
Other people have already offered a lot of good options. One that I didn't see specifically mentioned though is that you can technically use a semicolon (;
) as an inline statement separator and basically write whatever you want on a single line of code. For example:
array = []; if x; array << a; elsif y; array << b; else; array << c; end; array
Regarding question #2:
Here's a fun option to to add to the list. Use the chomp(separator)
, split
, and reverse
methods.
str1 = ""
str2 = "bar"
"#{str2},#{str1}".chomp(",").split(",").reverse
#=> ["bar"]
str1 = "foo"
str2 = "bar"
"#{str2},#{str1}".chomp(",").split(",").reverse
#=> ["foo", "bar"]
string.chomp(substring)
with and without reverse
can be super handy ways to format string results that would otherwise need some conditional statements to handle the same operation somewhere else in the chain.
精彩评论