Перед загрузкой в ClickHouse чищу выгрузку от дублей по паре клиент + дата. На 10 тысячах строк всё было мгновенно, на 200 тысячах скрипт крутится минут двадцать, процессор в сотку, память в норме. Ruby 3.4.
seen = []
unique = []
rows.each do |row|
key = [row["client_id"], row["event_date"]]
next if seen.include?(key)
seen << key
unique << row
end
Подозреваю include?, но хочу понять, как это правильно делается в Ruby, а не тащить привычки из Python.
Да, include? каждый раз проходит массив целиком, на 200 тысячах это квадрат. Нужен Set и его add?, который возвращает nil для уже виденного элемента:
require "benchmark"
seen = Set.new
Benchmark.realtime do
rows.select { |row| seen.add?([row["client_id"], row["event_date"]]) }
end
# массив и include?: 1184 с
# Set#add?: 0,21 с
Замерил на 200 тысячах сгенерированных строк с 15% дублей. require "set" с Ruby 3.2 не нужен.
Можно и в одну строку: rows.uniq { |r| r.values_at("client_id", "event_date") }. Внутри тоже хэш, а читается проще
В Python со списком вместо set будет ровно та же картина, язык ни при чём. Хотя в Go я бы сразу взял map со struct-ключом и не задумывался)
kirill_spb, uniq с блоком у меня вышел 0,24 с против 0,21 у add?, это в пределах шума. Я бы тоже взял uniq, он понятнее.