Ruby удаляет нечисловые значения из массива и конвертирует rest в float - PullRequest
2 голосов
/ 28 апреля 2019

У меня есть введенная пользователем строка, которую я превращаю в массив, затем перебираю массив и удаляю не числовые значения.

Похоже, мое совпадение с регулярным выражением работает наполовину, и мой to_f никогда не устанавливает значение массива в число с плавающей точкой.

Допустим, я ввожу: "1 2 3b c3 4, 5t"

puts "Enter Minutes"  
STDOUT.flush  
freq = gets.chomp
freq = freq.split(/\W/) #this creates the array, splitting at non-word chars

p freq #outputs: ["1", "2", "3b", "c3", "4", "", "5t"]

freq.each do |minutes|
        if ( minutes == "" or /\D/.match(minutes) ) then freq.delete(minutes) else minutes.to_f end
end

p freq #outputs: ["1", "2", "c3", "4", "5t"]

Мои желаемые результаты: [1, 2, 4] # примечание, что они числовые, а не символы

Ответы [ 2 ]

6 голосов
/ 28 апреля 2019

Проблема в том, что вы изменяете частоту только в состоянии then, а не в другом.

Существуют перечисляемые методы, которые изменяют вас, обычно они заканчиваются на !:

freq = ["1", "2", "3b", "c3", "4", "", "5t"]
=> ["1", "2", "3b", "c3", "4", "", "5t"]

freq.reject! { |minutes| minutes.match(/\D/) || minutes == "" }.map! { |minutes| minutes.to_f }
=> [1.0, 2.0, 4.0]
4 голосов
/ 28 апреля 2019
arr = ["1", "2", "3b", "c3", "4", "", "5t"]

Существует два возможных подхода к этой проблеме.Один из них состоит в том, чтобы выполнить два шага: удалить все элементы, которые не являются представлениями неотрицательных целых чисел, затем преобразовать эти целые числа в числа с плавающей запятой, требуя один проход через каждый из двух массивов.Другой заключается в создании массива чисел с плавающей запятой, совершающих один проход через arr.Это можно сделать следующим образом.

arr.each_with_object([]) { |s,a| a << s.to_f if s.match?(/\A\d+\z/) }
  #=> [1.0, 2.0, 4.0] 

Регулярное выражение гласит: «соответствует началу строки (якорь \A), за которым следуют одна или несколько цифр, за которыми следует конец строки(якорь \z). Это то же самое, что не совпадать с пустой строкой или символом, который не является цифрой, поэтому мы могли бы вместо этого написать:

arr.each_with_object([]) { |s,a| a << s.to_f unless s.empty? or s.match?(/\D/) }
  #=> [1.0, 2.0, 4.0] 

Выбор между этими двумя вариантами невеликздесь, но иногда последний подход легче реализовать.

Другой способ, который мне не слишком нравится (но о нем стоит знать), заключается в использовании метода Kernel # Float затем Array # compact :

arr = ["1", "-2", "3b", "c3", "4.23", "", "5t", "-1.2e3"]

arr.map { |s| Float(s) rescue nil }.compact
  #=> [1.0, -2.0, 4.23, -1200.0]

Как видно, это преобразует элементы arr, которые являются представлениями целых чисел или чисел с плавающей точкой (не только неотрицательных целых чисел), в числа с плавающей запятой (который может или не может быть необходим).

Float(s) поднимает ArgumentError, если s не может быть преобразован в число с плавающей точкой. Например:

Float("3b")
  #=> ArgumentError (invalid value for Float(): "3b")

Когда это происходит, япоймать исключение в строковом предложении по спасению , возвращая nil.

Некоторым Рубиестам не нравятся встроенные спасательные предложения, потому что они могут маскировать другие ошибки.Эта проблема может быть решена только спасением ArgumentErrors:

arr.map do |s|
  begin
    Float(s)
  rescue ArgumentError
    nil
  end
end.compact
  #=> [1.0, -2.0, 4.23, -1200.0]
...