...

воскресенье, 26 сентября 2021 г.

Впечатления от прохождения конкурса Яндекс «One Day Offer Frontend»


Делюсь впечатлениями как я поучаствовал в конкурсе Яндекс "One Day Offer Frontend". Суть в том, что надо пройти онлайн соревнование - решить за 3часа 5 задач, можно частично. Задачи на алгоритмы и верстку на 5, 45, 70, 90 и 90 баллов. Надо набрать >=100 баллов и тогда будет уже онлайн собес с людьми 25 сентября и оффер в одну из команд Я.Такси, Маркет или Поиск если все будет ОК.

Итак, мне на почту упало письмо от Я с предложением поучаствовать

2+ года назад я апплаился к ним на вакансию, но тогда что-то не срослось. Я за любую движуху, не каждый день такие письма приходят, да и сам принцип меритократии мне вполне по нраву, поэтому я в деле.

Для начала решил подготовиться

В письме была классная ссылка как проходят собесы в Я у фронтов: https://yandex.ru/recruitment-guide/frontend

Почитал теорию по структурам данных и алгоритмам (списки, деревья, графы, рекурсия, сортировки), освежил как считать оценку сложности, посмотрел видосики и порешал задания в тестовом контесте: https://contest.yandex.ru/contest/8458/problems/

Задачи из тестового соревнования:
A. Камни и украшения
B. Последовательно идущие единицы
C. Удаление дубликатов
D. Генерация скобочных последовательностей
E. Анаграммы
G. Интересное путешествие

Решил задачи A-E. Задача G была на поиск минимального пути в графе с доп условием. В принципе, ясно как решать - надо нагуглить и реализовать алгоритм: https://ru.wikipedia.org/wiki/Задача_о_кратчайшем_пути Но время поджимало, поэтому решил стартовать без него.

Еще пару скринов с решения

Мысли по результатам тестовых задач

  1. Надо тщательно подготовить рабочее место: хорошо выспаться, закрыться в комнате, чтобы ничего не отвлекало, принести воду/закуски, т.к. каждая минута на счету, открыть полезные вкладки в браузере, хороший интернет и т.п.

  2. Надо прям очень сильно не тупить, сразу врубаться в задачу, накидать на бумажке варианты решения, посчитать сложность выбранного алгоритма (O(n!) - ужасно, O(n^2) - плохо, O(logN) - отлично, O(1) - вы божественны!) и решать задачу сразу правильно оптимальным способом.

  3. Перед тем, как решать задачи кратко просмотреть их все, чтобы понять, какие осилишь, чтобы не тратить время на нерешаемые/трудоемкие варианты.

  4. Поставить себе таймлайн график - на какие задачи сколько времени выделяешь в минутах. Если тупишь с задачей, оставить ее и после минутного отдыха приступать к другой.

  5. Код на JS лучше писать тут: https://jsfiddle.net Там у меня не заработало полное решение, как его нужно было подавать в окно в Я.Контесте (со всеми этими "Напишите CommonJS-модуль c экспортируемой функцией" или "представьте себе, что с клавиатуры вам ввели символы"). Но в Я.Контесте можно написать "обвязку/каркас" решения, т.е. такой код:

    module.exports = function (str, symbol) {
    let result = 0;
    return result;
    }

    ... А затем вынести функцию и только эту функцию отладить в фиддлере:

    function countSymbol(str, symbol) {
    //debugger;
    var regex = new RegExp(symbol, 'g');
    let result = (str.match(regex) || []).length;
    return result;
    }

    ... И вставить ее назад в окно решений контеста.

  6. Лучше использовать браузер Хром, там точно работает дебаггер. Или надо написать самому в коде, чтобы остановилось где надо: debugger; Или вот так можно подебажить окно фиддлера: https://stackoverflow.com/questions/19460046/how-to-debug-the-js-in-jsfiddle Хотя как там у меня не сработало, поставил debugger; и у меня Хром сам остановился на странице с моим кодом: /_display/?editor_console=true А далее уже по F10 можно прыгать по брейкпоинтам куда надо.

  7. Лайфхак от меня (дарю!). Даже на очень-очень сложных задачах можно набрать баллы за частное решение за 5 секунд. Надо всего-то написать не код, решающий задачу, а код, проходящий часть тестов. Например, на сложной задаче на 90 баллов, которая должна принимать на входе json со структурой проекта (модули и тесты), где надо было выдать список тестов к измененным модулям я сразу закинул такое решение:

    module.exports = function (input) {
    return [];
    }

    ... и тут же набрал 15 из 90 баллов. Т.к. очевидно, что иногда даже для очень сложных случаев нужно выдать пустой массив тестов.

  8. Суперполезный совет в видео от разраба Я - начинать решение задачи с написания набора тестов.

    Т.е. чтобы ты понял, что:
    а) понимаешь, в каком виде придут входные данные
    б) понимаешь, какой результат будет успешный, если твоя функция корректно написана.

    Что-то в этом роде:

    let lines = ['qiu','iuq'];
    const [one, two] = lines;
    console.log(one);
    console.log(two);

    function areAnagrams(one, two) {
    let result = false;
    return result;
    }

    console.log(areAnagrams('qiu','iuq') == true);
    console.log(areAnagrams('q','q') == true);
    console.log(areAnagrams('','') == true);
    console.log(areAnagrams('zprl','zprc') == false);
    console.log(areAnagrams('zprl','z') == false);
    или так:
    function getTests(input) { return []; }

    console.log(JSON.stringify(getTests({})) == JSON.stringify([]));
    console.log(JSON.stringify(getTests({a: 'что то более сложное'})) == JSON.stringify(['/var/www/projects/project1/src/specs/1.js']));

  9. Фрустрация - даже правильно решенные задачи (как у меня пробные задачи C и D) могут признаваться ошибочными (и давать 0 баллов) с вердиктом: ML: Превышен лимит использования памяти. Т.е. кроме правильного решения надо писать еще и оптимально с т.з. памяти.

  10. Фрустрация 2 - для некоторых задач можно потерять уйму времени на то, чтобы закинуть в окно решения просто компилящееся без ошибок решение, пусть даже оно и выдает неверный результат. Имхо, тут перемудрили разрабы Я. Если проверяется знание алгоритмов, то лучше не заставлять угадывать, в каком виде ожидается результат, а дать сразу заготовку результата с пустым ответом такого вида:

    module.exports = function (input) {
    let result = [];
    return result;
    }

    или так:

    function generate(count) { return '((()))'; }

    const readline = require('readline');
    const rl = readline.createInterface({
    input: process.stdin
    });

    let lines = [];
    rl.on('line', (line) => {
    lines.push(line);
    }).on('close', () => {
    const [count] = lines;
    generate(count);
    });

    или так:

    class TaskManager {}
    module.exports = { TaskManager };

    Иногда, если сразу не можешь выдать компилящееся без ошибок решение, то нет смысла даже решать задачу, т.к. потратишь время, а баллов все равно наберешь 0. Так у меня произошло с последней задачей в реальном контесте «Джаваскриптович». Хотя, возможно, так и задумано. Если слишком много людей успешно решают задачи в контесте, то проблема с вводом/выводом данных - это доп условие, по которым, скажем, 30% отваливаются и до рекрутера доходят самые упор[н/от]ые :)


Теперь, собственно, сам процесс реального соревнования

Надеюсь, лонгрид вас еще не утомил.

Итак, 3 часа, 5 задач, минимум 100 баллов.

Были задачи:
A. Подсчитать количество вхождений символа в строку [5 баллов]
B. Ход конём [45 баллов]
C. Принцип «одного окна» [70 баллов]
D. Слишком надежный проект [90 баллов]
E. Межгалактический исследователь «Джаваскриптович» [90 баллов]

Текст задач и свой код по нему не даю, т.к.:

  1. задачи - интеллектуальная собственность Я

  2. возможно они будут использовать их в других соревнованиях и мой код даст неоправданное преимущество участникам, кто сможет нагуглить этот пост

  3. код писался в дикой спешке, я по ним получил нужные баллы, но в приличном обществе такое показывать нельзя

Поэтому объясню все словами на пальцах, если кто-то из Я обратится с просьбой убрать фрагмент текста, без проблем уберу.


A. Подсчитать количество вхождений символа в строку [5 баллов]

Тут все просто, не стал заморачиваться, регулярка, 2 минуты, 5 баллов в кармане.

B. Ход конём [45 баллов]

Надо было сверстать шахматную доску и по клику на ячейке покрасить ячейки вокруг, соответствующие ходу коня. При этом сохранять выделенные ячейки между кликами. Javascript использовать нельзя :(

На этой задаче я застрял надолго. Мозг отказывался решать, как это статичный HTML+CSS, самим Богом и Tim Berners-Lee не предназначенный для хранения состояний может таки хранить состояние если очень надо. В результате оставил задачу напоследок, когда время стало подходить к концу психанул и нагуглил аналогичное решение, подпилил его под условие задачи и получил заветные 45 баллов.

Спойлер решения

Под каждой ячейкой доски делаем невидимую радиокнопку, ее переключение и будет хранением состояний, а красим соседние ячейки через CSS тени объекта: input[type="radio"]:checked + label {background: #ff0000;box-shadow: -30px -60px 0 0 #0000ff...,}

C. Принцип «одного окна» [70 баллов]

Создать набор JS-роботов, которым скармливаем набор задач, они их исполняют и потом выдают саммари-лог со статистикой, кто какие задачи делал, какие успешно или нет, сколько каждый робот затратил времени на исполнение задач.

Эту задачу тоже оставил на конец. Поэтому сначала сделал простое решение. Роботы получают список задач, но исполняют их последовательно. Т.е. один берет задачу, остальные ждут, когда он закончил, он записывает в лог результат, идет курить и начинает работать следующий по очереди робот. Технологии Почты России в жизнь! Из-за нехватки времени это решение и ушло в продакшн и набрало 35 баллов. Очевидно, нужно было допилить, чтобы роботы работали параллельно - освободившийся брал задачи из пула и когда все закончат, дать ответ. При этом выход из бесконечного цикла был бы по условию: нет задач в очереди <И> все роботы освободились от работы (глобальная переменная?).

D. Слишком надежный проект [90 баллов]

Есть json со структурой проекта: список модулей, файлов кода в них и файлов тестов, настроенных на определенный код-файл и флаг, был ли изменен определенный модуль. Надо получить список тестов измененных модулей.

Эта задача показалась слишком простой. Сначала через лайфак №7 выдал пустой массив как решение:

module.exports = function (input) {
return [];
}

Сразу набрал 15 баллов. Для многих тестов пустой массив тестов и был решением. Далее бодро написал штук 7 тестов на json разной степени вложенности и сложности и запрограммил составленный алгоритм:

  1. заменить все алиасы "@/pages/b.js" на "@": "./src", -> "./src/pages/b.js" во всех видах инфо -file, deps

  2. алиасов может быть много, заменить по ходу все по порядку "@": "./src", "@2": "./src2", etc

  3. все пути сделать абсолютными - склеить с absoluteRepoPath: "/var/www/projects/project1", и/или урл модуля

  4. получить список измененных модулей hasChanged: true

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

  6. для измененных модулей получить список их deps - файлов содержащихся внутри них

  7. получить список тестов deps с пред. пункта измененных модулей

  8. отсортировать список тестов

Через 1-1.5ч работы добился, что все тесты у меня прошли успешно. Но Я.Контест показал все те же 15 из 90 баллов. Переделывал код несколько раз, но так и остались 15 баллов. Возможно, неверно выдавал результаты, например, нужно было не заменять пути в тестах на их алиасы или сортировать тесты по-другому и т.п.

E. Межгалактический исследователь «Джаваскриптович» [90 баллов]

Написать функцию, которая на входе принимает массив байтов = изображение с камеры. Далее перевести в градации серого, наложить на фото два фильтра, еще пара тройка манипуляций и выдать результат (определение контуров объектов) менее, чем за 50мс.

Тут были две проблемы:

  1. мне так и не удалось написать код, который бы всего лишь компилировался и без ошибок выдавал даже входной массив обратно без обработки. Что-то в этом роде:

    function run(arr, obj) {
    return new Promise((resolve, reject) => {
    // через 1 секунду готов результат: result
    setTimeout(() => resolve(arr), 10);
    // через 2 секунды — reject с ошибкой, он будет проигнорирован setTimeout(() => reject(new Error("ignored")), 2000);
    });
    }
    module.exports = { run };

  2. Я бы взял на такую задачу где-то неделю разработки. С учетом того, что нужно прочитать про фильтры, научиться с ними работать, подготовить изображения и тесты, которые корректно определяют границы и все это за 50мс. Сделать это за условные ~30-45мин, которые у меня оставались было маловероятно. Поэтому 0 баллов.

Итого, я набрал 5+45+35+15+0=100 баллов

Именно столько и нужно было для того, чтобы попасть на собес с живым сотрудником Я.


Собес с сотрудником я завалил

Код нужно было писать в текстовом редакторе без подчеркивания ошибок и гугла, т.е. по сути в уме.

Первая задача.
Написать функцию, которая загружает пару промисов и выдает результат.

Я решил.

Вторая задача.
Написать функцию flatten, которая сплющивает массив чего угодно "строк/функций/вложенных массивов" (аналог lodash/flatten) без использования рекурсии.

Время поджимало, поэтому написал что-то типа такого:
function flattenSubArray(arr) {
let result = [];
let hasSubArray = false;

for(let item of arr) {
if (Array.isArray(item)) {
result = result.concat(item);
hasSubArray = true;
} else {
result.push(item);
}
}

return [result, hasSubArray]
}

function flatten(arr) {
let result = [...arr];
let hasSubArray = false;
do {
[result, hasSubArray] = flattenSubArray(result);
} while(hasSubArray)

return result;
}

console.log(flatten([]));
console.log(flatten([1,2,3]));
console.log(flatten([1,'str', [2,3]]));
console.log(flatten([1,'str', [2, [3,'s', () => { return 4;}]]]));

Интервьюер сказал, что в коде есть ошибки, да и решение неоптимальное (~O(n^2)) и на этом собеседование закончилось. Это сейчас я по памяти код набрал, а тогда написал так: {result, hasSubArray} = flattenSubArray(result); т.е. фигурные скобки вместо квадратных. В этом была ошибка. Блокнот в Яндекс.Контесте или в чем я там писал код не подчеркивает ошибки. Сейчас проверил, код вроде норм, работает.

Что в итоге

Если ставить себе цель "попасть разработчиком в Яндекс", то задача выглядит решаемой.

Но я бы посоветовал взять 2-4 недели фуллтайм подготовки: алгоритмы, структуры данных, технические тонкости используемых технологий по вашему стеку (JS, HTML, CSS, ***). В Я, как я понял, новые проекты пишут на React+Typescript, есть свой виртуальный Гит, NodeJS, Flow, BEM, на старых проектах jQuery, но фронтов собеседуют на чистом JS, поэтому мой опыт в Angular особо роли не играл.

На этом все. Всем удачи и интересной работы!

Adblock test (Why?)

Комментариев нет:

Отправить комментарий