Метод цепочки блоков (Ruby) - PullRequest
0 голосов
/ 22 мая 2019

Учитывая следующий код:

    lines = Array.new() 
    File.foreach('file.csv').with_index do |line, line_num|                 
      lines.push(line.split(" ")) if line_num > 0                                 
    end                                                                                  

    indices = lines.map { |el| el.last }                                          
    duplicates = indices.select{ |e| indices.count(e) > 2 }.uniq

Пример CSV-файла выглядит так для всех, кто интересуется:

# Generated by tool XYZ
a b c 1
d e f 2
g h i 1
j k l 4
m n o 5
p q r 2
s t u 2
v w x 1
y z 0 5

Можно ли связать эти два блока методов (последние две строки кода) вместе?

Ответы [ 3 ]

2 голосов
/ 22 мая 2019

Если вы не хотите иметь промежуточную переменную и хотите сделать это в одной строке, вы можете написать что-то вроде этого:

duplicates = lines.group_by(&:last).select{|k, v| v.count > 2}.keys

Для некоторых людей это может помешать удобочитаемости! Просто зависит от твоего вкуса.

1 голос
/ 22 мая 2019

O(N) решение (один проход) будет выглядеть так:

lines.each_with_object([[], []]) do |el, (result, temp)|
  (temp.delete(el) ? result : temp) << el
end.first

Здесь мы используем промежуточный


Кроме того, вы всегда можете использовать Object#tap:

duplicates =
  lines.map(&:last).tap do |indices|
    indices.select { |e| indices.count(e) > 2 }.uniq
  end
0 голосов
/ 23 мая 2019

Пример

Давайте применим ваш код к примеру.

str =<<-END
Now is the
time for all
people who are
known to all
of us as the
best coders are
expected to
lead all
those who are
less experienced
to greatness
END

FName = 'temp'
File.write(FName, str)
  #=> 146

Ваш код

lines = Array.new() 
File.foreach(FName).with_index do |line, line_num|                 
  lines.push(line.split(" ")) if line_num > 0                                 
end                                                                                  
lines
  #=> [["time", "for", "all"], ["people", "who", "are"], ["known", "to", "all"],
  #    ["of", "us", "as", "the"], ["best", "coders", "are"], ["expected", "to"],
  #    ["lead", "all"], ["those", "who", "are"], ["less", "experienced"],
  #    ["to", "greatness"]] 
indices = lines.map { |el| el.last }                                          
  #=> ["all", "are", "all", "the", "are", "to", "all", "are", "experienced", "greatness"] 
duplicates = indices.select { |e| indices.count(e) > 2 }
  #=> ["all", "are", "all", "are", "all", "are"] 
duplicates.uniq
  #=> ["all", "are"] 

Считается, что объект возвращает массив всех слов, которые появляются как последнее слово строки (кроме первой строки) более двух раз.

Более Ruby-подобный и более эффективный код

Мы можем сделать это более кратко и эффективно, сделав один проход по файлу:

first_line = true
h = Hash.new(0)
File.foreach(FName) do |line|
  if first_line
    first_line = false
  else
    h[line[/\S+(?=\n)/]] += 1
  end
end
h.select { |_,count| count > 2 }.keys
  #=> ["all", "are"]

Выполненные шаги

Шаги следующие.

first_line = true
h = Hash.new(0)
File.foreach(FName) do |line|
  if first_line
    first_line = false
  else
    h[line[/\S+(?=\n)/]] += 1
  end
end
h #=> {"all"=>3, "are"=>3, "the"=>1, "to"=>1, "experienced"=>1, "greatness"=>1}
g = h.select { |_,count| count > 2 }
  #=> {"all"=>3, "are"=>3} 
g.keys
  #=> ["all", "are"]

Использование Перечислитель # each_object

Вместо того, чтобы определять хеш до выполнения File.foreach(..), обычно используется метод Enumerator#each_object, который позволяет нам связать хэш, который построен для следующих операторов:

first_line = true
File.foreach(FName).with_object(Hash.new(0)) do |line, h|
  if first_line
    first_line = false
  else
    h[line[/\S+(?=\n)/]] += 1
  end
end.select { |_,count| count > 2 }.keys
  #=> ["all", "are"] 

Использование счетного хэша

Я определяю хеш следующим образом.

h = Hash.new(0)

Используется форма Hash :: new , которая определяет значение по умолчанию , равное new s аргументу. Если h = Hash.new(0) и h не имеют ключа k, h[k] возвращает значение по умолчанию, ноль. Парсер Ruby расширяет выражение h[k] += 1 до:

h[k] = h[k] + 1

Если h не имеет ключа k, выражение становится

h[k] = 0 + 1

Обратите внимание, что h[k] = h[k] + 1 является сокращением для:

h.[]=(k, h.[](k) + 1)

Метод Hash#[] по умолчанию равен нулю, а не метод Hash#[]=.

Использование регулярного выражения для извлечения последнего слова каждой строки

Одна из строк

str = "known to all\n"

Мы можем использовать регулярное выражение r = /\S+(?=\n)/, чтобы извлечь последнее слово:

str[r] #=> "all"

Регулярное выражение гласит: «соответствует одному или нескольким (+) символам, которые не являются пробельными символами (\S), сразу же после символа новой строки. (?=\n) - это положительный прогноз . "\n" должно совпадать, поскольку оно не является частью возвращенного совпадения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...