ruby удаляет дубликаты и изменяет существующий элемент во вложенном массиве, перебирая другой массив - PullRequest
2 голосов
/ 09 января 2020

У меня есть массив doc_count , который среди прочего содержит строку с датой (год-месяц-день).

Я хотел бы преобразовать doc_count в цель путем удаления «дубликатов», что означает, что я хотел бы сохранить более длинную строку даты и удалить короткую строку даты, например.

"2019-02-01 : 186904 "вместо" 2019-02-01 "

  doc_count = [
    ["foo", "2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01"],
    ["bar", "2019-01-01: 8876", "2019-04-01: 8694", "2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01"]
  ]

  goal = [
    ["foo", "2019-01-01", "2019-02-01: 186904", "2019-03-01: 196961", "2019-04-01"],
    ["bar", "2019-01-01: 8876", "2019-02-01", "2019-03-01", "2019-04-01: 8694"]
  ]

   month.match(/^\d{4}-\d{2}-\d{2}/) && month.include?(': ') ? 
     month.match(/^\d{4}-\d{2}-\d{2}/)[0] : month

  my_attempt = doc_count.each do |topic|
    topic.each do |el|
      topic.delete(el) if el == string_to_month(el)
    end
  end

По какой-то причине моей попытке не удалось создать массив, идентичный цели .

  2.6.3 (main):0 > my_attempt       
  => [
    [0] [
      [0] "foo",
      [1] "2019-02-01: 186904",
      [2] "2019-03-01: 196961",
      [3] "2019-02-01",
      [4] "2019-04-01"
    ],
    [1] [
      [0] "bar",
      [1] "2019-01-01: 8876",
      [2] "2019-04-01: 8694",
      [3] "2019-02-01",
      [4] "2019-04-01"
    ]
  ]

Как я могу это исправить? Большое спасибо!

Ответы [ 2 ]

2 голосов
/ 09 января 2020

Одним из решений может быть сочетание Array # flat_map und # max_by

. Метод #flat_map возвращает новый массив с объединенными результатами выполнения блока один раз для каждый элемент и #max_by массив максимальных элементов. Вы уже использовали #match для проверки формата даты, но в этом примере нет необходимости перемещать его в отдельном методе.

solution = doc_count.map do |topic|
  topic.group_by { |s| s[0..9] }.flat_map do 
    |key, values| key.match?(/^\d{4}-\d{2}-\d{2}/) ? [values.max_by(&:size)] : values 
  end.sort.rotate!(-1)
end

последний, но не менее важный: #sort и #rotate (-1), чтобы получить желаемый порядок сортировки массива.

ОБНОВЛЕНИЕ: пожалуйста, используйте решение Кэри Свовеланд, лучше, и он проделал необычайную работу, чтобы подробно объяснить шаги.

1 голос
/ 09 января 2020
doc_count = [
  ["foo", "2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01", 
   "2019-02-01", "2019-03-01", "2019-04-01"],
  ["bar", "2019-01-01: 8876", "2019-04-01: 8694", "2019-01-01",
   "2019-02-01", "2019-03-01", "2019-04-01"]
]

Мы можем написать

def doit(doc_count)
  doc_count.map do |arr|
    date_strings, other_strings =
      arr.partition { |s| s.match? /\A\d{4}-\d{2}-\d{2}(?::|\z)/ }
    other_strings + select_dates(date_strings)
  end
end

, где select_dates - метод, который еще предстоит построить.

Вычисления для doc_count[0] следующие:

arr = doc_count[0]
  #=> ["foo", "2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
  #    "2019-02-01", "2019-03-01", "2019-04-01"] 
date_strings, other_strings =
  arr.partition { |s| s.match? /\A\d{4}-\d{2}-\d{2}(?::|\z)/ }
  #=> [["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
  #     "2019-02-01", "2019-03-01", "2019-04-01"], ["foo"]] 
date_strings
  #=> ["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
  #    "2019-02-01", "2019-03-01", "2019-04-01"] 
other_strings
  #=> ["foo"] 

Расчеты для второго элемента doc_count аналогичны. См. Перечислимый # раздел .

Теперь я дам два способа построения метода select_dates, первый из которых более эффективен, а второй, возможно, более прост.

Используйте форму Hash # update (он же merge!), который использует блок для определения значений ключей, которые присутствуют в обоих объединяемых хэшах

def select_dates(date_strings)
  date_strings.each_with_object({}) do |s,h|
    h.update(s[0, 10]=>s) { |_,o,n| n.size >= o.size ? n : o }
  end.values
end

См. Объяснения do c переменных блока _, o и n (_ - допустимая локальная переменная - используется для первой переменной блока, чтобы сообщить читателю, что она не используется в расчете блока). Для date_strings, указанного выше для doc_count[0]

select_dates(date_strings)
  #=> ["2019-02-01: 186904", "2019-03-01: 196961", "2019-01-01",
  #    "2019-04-01"] 

Расчеты следующие.

 enum = date_strings.each_with_object({})
   #=> #<Enumerator: ["2019-02-01: 186904", "2019-03-01: 196961",
   #   "2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01"
   #                 ]:each_with_object({})> 

 s,h = enum.next
   #=> ["2019-02-01: 186904", {}] 
 s #=> "2019-02-01: 186904" 
 h #=> {} 
 key = s[0, 10]
   #=> "2019-02-01" 
 h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
   #=> {"2019-02-01"=>"2019-02-01: 186904"} 

 s,h = enum.next
   #=> ["2019-03-01: 196961", {"2019-02-01"=>"2019-02-01: 186904"}] 
 key = s[0, 10]
   #=> "2019-03-01" 
 h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
   #=> {"2019-02-01"=>"2019-02-01: 186904",
   #    "2019-03-01"=>"2019-03-01: 196961"} 

 s,h = enum.next
   #=> ["2019-01-01", {"2019-02-01"=>"2019-02-01: 186904",
   #    "2019-03-01"=>"2019-03-01: 196961"}] 
 key = s[0, 10]
   #=> "2019-01-01" 
 h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
   #=> {"2019-02-01"=>"2019-02-01: 186904",
   #    "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"}

 s,h = enum.next
   #=> ["2019-02-01", {"2019-02-01"=>"2019-02-01: 186904",
   #    "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"}] 
 key = s[0, 10]
   #=> "2019-02-01" 
 h.update(key=>s) { |_,o,n| n.size >= o.size ? n : o }
   #=> {"2019-02-01"=>"2019-02-01: 186904",
   #    "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01"} 

Для первых трех элементов enum, которые генерируются и передаются в блок, блок update не вступает в игру, так как объединяемые два хэша (h и { key=>s }) не имеют общего ключа. Для четвертого элемента ("2019-02-01"), который присутствует в обоих объединяемых хэшах, мы отсылаем к блоку для сравнения h["2019-02-01"].size #=> "2019-02-01: 186904".size => 18 с "2019-02-01".size #=> 10. Поскольку первое больше, мы сохраняем его как значение "2019-02-01" в h. Остальные расчеты для update аналогичны, в результате чего:

h #=>  ["2019-02-01"=>"2019-02-01: 186904",
  #     "2019-03-01"=>"2019-03-01: 196961", "2019-01-01"=>"2019-01-01",
  #     "2019-04-01"=>"2019-04-01" }

Последний шаг заключается в извлечении значений из этого га sh (h.values).

Используйте Array # uniq

def select_dates(date_strings)
  date_strings.sort_by(&:size).reverse.uniq { |s| s[0, 10] }
end

Для date_strings, приведенного выше для doc_count[0]

select_dates(date_strings)
  #=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
  #    "2019-01-01"] 

Вычисления следующие.

a = date_strings.sort_by(&:size)
  #=> ["2019-01-01", "2019-02-01", "2019-03-01", "2019-04-01",
  #    "2019-02-01: 186904", "2019-03-01: 196961"] 
b = a.reverse
  #=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
  #    "2019-03-01", "2019-02-01", "2019-01-01"] 
b.uniq { |s| s[0, 10] }
  #=> ["2019-03-01: 196961", "2019-02-01: 186904", "2019-04-01",
  #    "2019-01-01"] 

Обратите внимание, что do c для Array#uniq говорит о том, что «self пройден по порядку, и первое вхождение сохраняется». Выражение

sort_by(&:size).reverse

можно заменить на

sort_by { |s| -s.size }

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

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