开发者

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:

  1. Is there a way to conditionally add to an array in one line?
  2. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜