ENIGMA AI
ENIGMA AI

Сформулируй задачу на Haskell: по таблице users (история кликов клиентов по категориям) и заданному user_id вывести самую популярную категорию для этого пользователя (если частоты одинаковые — любую).

встречается 1× Haskell middle language_specific

Как ответить

Задача сводится к группировке записей таблицы users по категориям для заданного пользователя, подсчёту количества кликов в каждой группе и выбору категории с максимальным значением. В Haskell это естественно решается через комбинацию filter, groupBy (или group после сортировки) и maximumBy.

Допустим, таблица users представлена списком кортежей (Int, String)(userId, category). Тогда решение будет выглядеть так:

import Data.List (group, sort, maximumBy)
import Data.Ord (comparing)

mostPopularCategory :: Int -> [(Int, String)] -> String
mostPopularCategory uid rows =
  let userRows = filter ((== uid) . fst) rows
      categories = map snd userRows
      grouped = group (sort categories)
      counted = map (\g -> (head g, length g)) grouped
      (bestCat, _) = maximumBy (comparing snd) counted
  in bestCat

Разбор по шагам:

  • Фильтрацияfilter ((== uid) . fst) rows оставляет только строки нужного пользователя.
  • Извлечение категорийmap snd userRows даёт список категорий для этого пользователя.
  • Группировкаgroup (sort categories) собирает одинаковые категории в подсписки. Сортировка обязательна, иначе group сгруппирует только соседние элементы.
  • Подсчётmap (\g -> (head g, length g)) превращает каждую группу в пару (категория, количество).
  • Выбор максимумаmaximumBy (comparing snd) находит пару с наибольшим вторым элементом.

Если частоты одинаковые, maximumBy вернёт первую попавшуюся максимальную категорию — это удовлетворяет условию «любую». Можно уточнить поведение: если список после фильтрации пуст (пользователь не найден), код упадёт с ошибкой. На практике стоит добавить обработку через Maybe:

mostPopularCategorySafe :: Int -> [(Int, String)] -> Maybe String
mostPopularCategorySafe uid rows = case counted of
  [] -> Nothing
  _  -> Just bestCat
  where
    userRows = filter ((== uid) . fst) rows
    categories = map snd userRows
    grouped = group (sort categories)
    counted = map (\g -> (head g, length g)) grouped
    (bestCat, _) = maximumBy (comparing snd) counted

Для больших объёмов данных (миллионы строк) лучше использовать Data.Map.Strict с аккумулятором — это даст O(n log n) вместо O(n log n) через сортировку, но с меньшими константами и без лишнего хранения:

import qualified Data.Map.Strict as Map

mostPopularCategoryMap :: Int -> [(Int, String)] -> String
mostPopularCategoryMap uid rows =
  let userRows = filter ((== uid) . fst) rows
      freqMap = Map.fromListWith (+) [(cat, 1) | (_, cat) <- userRows]
      (bestCat, _) = maximumBy (comparing snd) (Map.toList freqMap)
  in bestCat

В реальном проекте я бы выбрал второй вариант: он короче, не требует сортировки и легко адаптируется под потоковую обработку (например, через foldl').

Ключевые тезисы

  • Фильтрация записей по user_id, затем группировка по категориям и подсчёт частот.
  • Использование group + sort или Map.fromListWith для подсчёта — второй вариант предпочтительнее по производительности.
  • Выбор максимума через maximumBy с comparing snd; при равенстве возвращается первая найденная категория.
  • Обработка пустого результата через Maybe, чтобы избежать ошибок выполнения.

Что спросят дальше

  • — Как изменится решение, если нужно вернуть не одну категорию, а список всех категорий с максимальной частотой?
  • — Оцени сложность по времени и памяти для варианта с Map и для варианта с group/sort.
  • — Что делать, если таблица users не влезает в память? Как переписать решение для потоковой обработки?

Готовьтесь к собеседованию с ENIGMA AI

AI-суфлёр подсказывает ответы прямо на собеседовании в реальном времени — незаметно для интервьюера.

Скачать приложение