операторы ruby ​​do, если хеш-ключ удовлетворяет условию - PullRequest
0 голосов
/ 21 января 2019

у меня есть:

event = {"first_type_a" => 100, "first_type_b" => false, "second_type_a" => "abc", "second_type_b" => false}

Я пытаюсь изменить ключи event на основе оригинала. Если имя содержит подстроку, имя будет изменено, и новый ключ, значение пары должно быть добавлено, а старая пара должна быть удалена. Я ожидаю получить:

event = {"important.a_1" => 100, "important.b_1" => false, "second_type_a" => "abc", "second_type_b" => false}

Какой самый эффективный способ обновить event?

Я ожидал, что это сработает:

event.each_pair { |k, v|
  if !!(/first_type_*/  =~ k) do
        key = "#{["important", k.split("_", 3)[2]].join(".")}";
        event.merge!(key: v);
        event.delete(k)  
  end
}

но возникает ошибка:

  simpleLoop.rb:5: syntax error, unexpected keyword_do, expecting keyword_then or ';' or '\n'
  ... if !!(/first_type_*/  =~ k) do
  ...                             ^~
  simpleLoop.rb:9: syntax error, unexpected keyword_end, expecting '}'
    end
    ^~~
  simpleLoop.rb:21: embedded document meets end of file

Я подумал подойти к нему по-другому:

if result = event.keys.find { |k| k.include? "first_type_" } [
  key = "#{["important", k.split("_", 3)[2]].join(".")}"
  event.merge!(key: v)
  event.delete(k)
]

но все равно не повезло. Я считаю все скобки, и, как показывает ошибка, это что-то есть, но я не могу найти это. Имеет ли значение заказ?

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Я просто покажу, как это можно сделать довольно экономично в Ruby.До недавнего времени при желании изменить ключи хеша обычно делали одно из двух:

  • создавали новый пустой хеш, а затем добавляли пары ключ / значение в хеш;или
  • преобразовать хэш в массив a двухэлементных массивов (пар ключ-значение), изменить первый элемент каждого элемента a (ключ) и затем преобразовать a внужный хэш.

Недавно (с помощью MRI v2.4) монахи-рубины даровали нам удобные методы Hash # transform_keys и Hash # transform_keys! ,Мы можем использовать первый из них с выгодой здесь.Сначала нам нужно регулярное выражение для сопоставления ключей.

r = /
    \A           # match beginning of string
    first_type_  # match string
    (\p{Lower}+) # match 1+ lowercase letters in capture group 1
    \z           # match the end of the string
    /x           # free-spacing regex definition mode

Условно это пишется

r = /\Afirst_type_(\p{Lower}+)\z/

Использование свободный интервал делает регулярное выражение самостоятельно-documenting.Теперь мы применяем метод transform_keys вместе с методом String # sub и только что определенным регулярным выражением.

event = {"first_type_a"=>100, "first_type_b"=>false,
         "second_type_a"=>"abc", "second_type_b"=>false}

event.transform_keys { |k| k.sub(r, "important.#{'\1'}_1") }
  #=> {"important.a_1"=>100, "important.b_1"=>false,
  #    "second_type_a"=>"abc", "second_type_b"=>false} 

В регулярном выражении конструкция p {} выражение \p{Lower} можно заменить на \p{L}, выражение в скобках POSIX [[:lower:]] (оба соответствуют буквам Unicode) или [a-z], но последнее имеет тот недостаток, что оно не будет совпадать с буквамидиакритические знакиЭто включает в себя буквы слов, заимствованные из других языков, которые используются в английском тексте (например, розовое, вино).Ищите Regexp для документации выражений POSIX и \p{}.

Если за "first_type_" могут следовать строчные или прописные буквы, используйте \p{Alpha};если после него могут следовать буквенно-цифровые символы, используйте \p{Alnum} и т. д.

0 голосов
/ 22 января 2019

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

def transform(str)
  return ['important.', str.split('_').last, '_1'].join() if str[0..4] == 'first' # I checked just for "first"
  str
end

event.transform_keys! { |k| transform(k) } # Ruby >= 2.5 
event.map { |k, v| [transform(k), v] }.to_h # Ruby < 2.5 

С использованием Hash#each_pair, но с Enumerable#each_with_object:

event.each_pair.with_object({}) { |(k, v), h| h[transform(k)] = v }

Или использовать в качестве одного вкладыша:

event.transform_keys { |str| str[0..4] == 'first' ? ['important.', str.split('_').last, '_1'].join() : str  }
0 голосов
/ 22 января 2019

if начинает блок, такой как другие структуры if ... else ... end do ... end begin ... rescue ... end

Поэтому ваш первый пример удаляет do после if, блок уже открыт. Я также сделал это более понятным, изменив блок после each_pair на использование do ... end вместо фигурных скобок, чтобы избежать путаницы с хешем с блоком.

event = { 'first_type_a' => 100, 'first_type_b' => false, 'second_type_a' => 'abc', 'second_type_b' => false }
new_event = {}
event.each_pair do |k, v|
  if !!(/first_type_*/  =~ k)
    important_key = ['important', k.split('_', 3)[2]].join('.')
    new_event[important_key] = v
  else
    new_event[k] = v
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...