【问题标题】:How can I convert a human-readable number to a computer-readable number in Ruby?如何在 Ruby 中将人类可读的数字转换为计算机可读的数字?
【发布时间】:2014-07-07 21:16:13
【问题描述】:

我正在使用 Ruby 处理一个数组,该数组包含一系列人类可读格式的数字(例如,2.5B、1.27M、600,000,其中“B”代表十亿,“M”代表百万)。我正在尝试将数组的所有元素转换为相同的格式。

这是我写的代码:

array.each do |elem|
    if elem.include? 'B'
        elem.slice! "B"
        elem = elem.to_f
        elem = (elem * 1000000000)
    else if elem.include? 'M'
        elem.slice! "M"
        elem = elem.to_f
        elem = (elem * 1000000)
    end
end

但是,当我使用 puts(array) 检查数组的元素时,数字显示为“B”和“M”被切掉,但似乎没有应用乘法转换(例如,现在读取的数字2.5、1.27、600,000,而不是 2500000000、1270000、600,000)。

我做错了什么?

【问题讨论】:

  • 这是个好问题。切片!有效,因为它改变了数组元素,但是 assingment elem = ... 你现在引用了一个不同的对象。请参阅下面的@Thilo 的答案以获得一个不错的选择。
  • 您需要提供一个输入数组样本。不要让人们发明输入。
  • 感谢大家的指导。很有帮助。
  • 如果您发现任何答案有帮助,请选择最有帮助的答案。

标签: ruby arrays


【解决方案1】:

首先要注意的是,ruby 中的else ifelsif。见http://www.tutorialspoint.com/ruby/ruby_if_else.htm

这里有一个工作功能供您试用:

def convert_array_items_from_human_to_integers(array)

    array.each_with_index do |elem,i|
        if elem.include? 'B'
            elem.slice! "B"
            elem = elem.to_f
            elem = (elem * 1000000000)
        elsif elem.include? 'M'
            elem.slice! "M"
            elem = elem.to_f
            elem = (elem * 1000000)
        end
        array[i] = elem
    end

    return array 

end

致电convert_array_items_from_human_to_integers(["2.5B", "1.2M"])

返回[2500000000.0, 1200000.0]

【讨论】:

    【解决方案2】:

    另一种变化:

    array = ['2.5B', '1.27M', '$600000']
    
    p array.each_with_object([]) { |i, a|
      i = i.gsub('$', '')
      a << if i.include? 'B'
        i.to_f * 1E9
      elsif i.include? 'M'
        i.to_f * 1E6
      else
        i.to_f
      end
    }
    

    #=&gt; [2500000000.0, 1270000.0, 600000.0]

    【讨论】:

      【解决方案3】:

      试试这个:

      array.map do |elem|
          elem = elem.gsub('$','')
          if elem.include? 'B'
              elem.to_f * 1000000000
          elsif elem.include? 'M'
              elem.to_f * 1000000
          else
              elem.to_f
          end
      end
      

      这使用map 而不是each 来返回一个新数组。您的尝试分配了数组元素的副本,将原始数组保留在原地(slice! 除外,它会在原地修改)。您可以一开始就放弃切片,因为to_f 将简单地忽略任何非数字字符。

      编辑:

      如果您有诸如$2.5B 之类的前导字符,正如您的问题标题所示(但不是您的示例),您需要明确删除这些字符。但是您的示例代码也不能处理这些,所以我认为这不是问题。

      【讨论】:

      • "$2.5B".to_f # =&gt; 0.0
      • @user3814019,数组也可以用map!的新元素替换
      【解决方案4】:

      稍微扩展一下 pjs 的回答:

      array.each do |elem|
      

      elem 是一个局部变量,指向每个数组元素,一次一个。当你这样做时:

              elem.slice! "B"
      

      您正在向该数组元素发送一条消息,告诉它对 B 进行切片。您会在最终结果中看到这一点。但是当你这样做时:

              elem = elem.to_f
      

      现在您已将局部变量 elem 重新分配给全新的东西。您没有重新分配数组中的内容,只是 elem 是什么。

      【讨论】:

        【解决方案5】:

        下面是我的做法:

        ARY = %w[2.5B 1.27M 600,000]
        
        def clean_number(s)
          s.gsub(/[^\d.]+/, '')
        end
        
        
        ARY.map{ |v|
        
          case v
          when /b$/i
            clean_number(v).to_f * 1_000_000_000
          when /m$/i
            clean_number(v).to_f * 1_000_000
          else
            clean_number(v).to_f
          end
        
        }
        # => [2500000000.0, 1270000.0, 600000.0]
        

        代码的核心在case 语句中。对乘数的简单检查允许我去除不需要的字符并乘以正确的值。

        通常我们可以使用to_f 来查找要与“1.2”之类的字符串相乘的浮点数,但对于“$1.2M”之类的字符串,它会因为“$”而分解。嵌入式逗号标记千位也是如此:

        '$1.2M'.to_f # => 0.0
        '1.2M'.to_f # => 1.2
        '6,000'.to_f # => 6.0
        '6000'.to_f # => 6000.0
        

        要解决仅包含值的简单字符串的问题,没有必要做任何比使用gsub(/[^\d.]+/, '') 去除不需要的字符更花哨的事情:

        '$1​​.2M'.gsub(/[^\d.]+/, '') # => "1.2" '1.2M'.gsub(/[^\d.]+/, '') # => "1.2" '6,000'.gsub(/[^\d.]+/, '') # => "6000" '6000'.gsub(/[^\d.]+/, '') # => "6000"

        [^\d.] 表示“任何数字或'.'

        注意如何将十进制值转换为整数。你最终可能会丢掉重要的精度:

         '0.2M'.gsub(/[^\d.]+/, '').to_f * 1_000_000 # => 200000.0
         '0.2M'.gsub(/[^\d.]+/, '').to_i * 1_000_000 # => 0
        ('0.2M'.gsub(/[^\d.]+/, '').to_f * 1_000_000).to_i  # => 200000
        

        当然,如果您的字符串比简单的数字和乘数更复杂,那么所有这些都会失效。分解字符串并识别这些子字符串很容易,但这是一个不同的问题。

        【讨论】:

          【解决方案6】:

          我会这样做:

          代码

          T, M, B = 1_000, 1_000_000, 1_000_000_000
          
          def convert(arr)
            arr.map do |n|
              m = n.gsub(/[^\d.TMB]/,'')
              m.to_f * (m[-1][/[TMB]/] ? Object.const_get(m[-1]) : 1)
            end
          end
          

          示例

          arr = %w[$2.5B 1.27M 22.5T, 600,000]
          convert(arr)    
            # => [2500000000.0, 1270000.0, 22500.0, 600000.0]
          

          说明

          线

          m = n.gsub(/[^\d.TMB]/,'')
            # => ["2.5B", "1.27M", "22.5T", "600000"]
          

          仅仅消除不需要的字符。

            m.to_f * (m[-1][/[TMB]/] ? Object.const_get(m[-1]) : 1)
          

          如果该字符是TMB,则返回转换为浮点数的字符串与字符串最后一个字符给出的常量的乘积,否则返回1

          实际的实现可能是这样的:

          class A
            T, M, B = 1_000, 1_000_000, 1_000_000_000
            def doit(arr)
              c = self.class.constants.map(&:to_s).join
              arr.map do |n|
                m = n.gsub(/[^\d.#{c}]/,'')
                m.to_f * (m[-1][/[#{c}]/] ? self.class.const_get(m[-1]) : 1)
              end
            end
          end
          

          如果我们希望将 1,000 的引用从 T 更改为 K 并添加 T 以表示万亿,我们只需更改

            T, M, B = 1_000, 1_000_000, 1_000_000_000
          

            K, M, B, T = 1_000, 1_000_000, 1_000_000_000, 1_000_000_000_000
          

          【讨论】:

            猜你喜欢
            • 2013-02-09
            • 1970-01-01
            • 2014-12-24
            • 1970-01-01
            • 2013-01-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-11-28
            相关资源
            最近更新 更多