【问题标题】:Initialize an Array with optional elements depending on conditions根据条件使用可选元素初始化数组
【发布时间】:2021-01-28 16:42:47
【问题描述】:

当你想用ruby/rails中的可选元素初始化一个数组时,常用的方法是什么?

现在可以

array = []
array << "1st element"
array << "2nd element" if flag?
array << "3rd element"

但是感觉很冗长,我猜它可能比完全初始化时要慢

我想知道有没有类似的,

array = [
  "1st element",
  "2nd element" if flag?,
  "3rd element",
]
#=> ["1st element", "3rd element"]

【问题讨论】:

    标签: arrays ruby-on-rails ruby


    【解决方案1】:

    您可以使用 splat 运算符 * 来实现此目的:

    array = [
      "1st element",
      *("2nd element" if flag?),
      "3rd element"
    ]
    

    这是因为*"2nd element" 返回字符串而*nil 有效地删除了自己:

    ["1st element", *"2nd element", "3rd element"]
    #=> ["1st element", "2nd element", "3rd element"]
    
    ["1st element", *nil, "3rd element"]
    #=> ["1st element", "3rd element"]
    

    请注意,* 会尝试调用 to_a,因此如果您的对象实现该方法,这可能会产生不良影响:(这是 Time#to_a

    ["1st element", *Time.now, "3rd element"]
    #=> ["1st element", 56, 43, 11, 15, 10, 2020, 4, 289, true, "CEST", "3rd element"]
    

    【讨论】:

      【解决方案2】:

      我会强调清晰性和通用性。

      a = [1,2,3]
      b = [4,5,6]
      c = [7,8,9]
      
      def concat_some(*arr)
        arr.reduce([]) { |ar,(a,flag)| flag ? ar.concat(a) : ar }
      end
      
      concat_some([a,true], [b,false], [c,true])
        #=> [1, 2, 3, 7, 8, 9]
      concat_some([a,false], [b,true], [c,false])
        #=> [4, 5, 6]
      

      def concat_for_indices(*arr, indices)
        indices.reduce([]) { |a,i| a.concat(arr[i]) }
      end
      
      concat_for_indices(a,b,c,[1,2])
        #=> [4, 5, 6, 7, 8, 9]
      concat_for_indices(a,b,c,[2])
        #=> [7, 8, 9] 
      concat_for_indices(a,b,c,[])
        #=> [] 
      concat_for_indices(a,b,c,[0,1,2])
        #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
      

      def concat_except_for_indices(*arr, indices)
        ((0..arr.size-1).to_a - indices).reduce([]) { |a,i| a.concat(arr[i]) }
      end
      
      concat_except_for_indices(a,b,c, [0])
        #=> [4, 5, 6, 7, 8, 9] 
      concat_except_for_indices(a,b,c, [1,2])
        #=> [1, 2, 3] 
      concat_except_for_indices(a,b,c, [])
        #=> [1, 2, 3, 4, 5, 6, 7, 8, 9] 
      concat_except_for_indices(a,b,c, [0,1,2])
        #=> []
      

      【讨论】:

        【解决方案3】:

        虽然这在初始化时并不能完全做到这一点,但如果您知道索引,您可以随时 insert 之后的元素:

        array = ["1st element", "3rd element"]
        array.insert(1, "2nd element") if flag?
        

        或者更优雅一点:

        array = ["1st element", "3rd element"].tap { |a| a.insert(1, "2nd element") if flag? }
        

        这里的优点是它不会压缩数组,以防您实际上打算使用 nil 值。

        【讨论】:

          【解决方案4】:

          虽然这不是一个好主意,但你可以在 ruby​​/rails 中做这样的事情:

          flag = true
          ["1st element", eval("'2nd element' if flag"), "3rd element"].compact 
          # output: ["1st element", "2nd element", "3rd element"]
          

          【讨论】:

            【解决方案5】:

            我不确定这是否是一种好方法,但这是我的建议:

            array = [
              "1st element",
              flag? ? "2nd element" : nil,
              "3rd element"
            ].compact
            

            .compact 将从您的数组中删除所有 nil 值。如果您可能有 nil 值,这将是有问题的。

            【讨论】:

            • 您可能希望使用compact! 来避免创建临时数组。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-01-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-23
            相关资源
            最近更新 更多