Как Яндекс научил меня собеседовать программистов

    После того, как я изложил свою историю «трудоустройства» в Яндекс в комменте к нашумевшей заметке «Как я проработала 3 месяца в Я.Маркете и уволилась», было бы несправедливо утаить и ту пользу, которую я вынес из своего опыта Яндекс.Собеседования.

    В мои рабочие обязанности входит техническое интервьюирование кандидатов на позицию Fullstack JavaScript/TypeScript Developer, активно этим делом (стоит ли говорить, что слегка поднадоевшим?) я занимаюсь больше года, за плечами более 30 технических интервью.

    Раньше на техническом интервью я задавал кандидату довольно бестолковые вопросы аля «что такое замыкание», «как в JavaScript реализуется наследование», «вот есть такая-то таблица в БД с такими-то индексами, расскажите, пожалуйста, как можно ускорить такой-то запрос», которые хоть и помогали выявить инженерные способности кандидата, но совершенно не позволяли сделать вывод о том, насколько хорошо человек сможет решать задачи и насколько быстро он сможет разобраться в уже существующем коде. Что не могло не привести к печальным последствиям…

    Но всё поменялось после того, как я прошёл четыре круга технических собеседований в Яндексе.

    Обычно в огород интервьюеров Яндекса летят камни за:

    1. Задачи, которые не имеют практической ценности;
    2. Необходимость решать эти задачи на листках бумаги карандашом или на доске.

    Уже 2019-й год и настала пора запускать в аду отдельную производственную линию по отливу котлов для тех, кто заставляет людей писать от руки текст, не говоря уже о коде. Каждый человек пишет по-разному, и мне, подготавливая текст к этой заметке, пришлось переписать, например, конкретно вот этот абзац шесть раз — если бы я писал заметки для Хабра на бумаге, я бы не писал заметки для Хабра.

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

    /* Необходимо реализовать функцию getRanges, которая возвращает следующие результаты: */
    getRanges([0, 1, 2, 3, 4, 7, 8, 10]) // "0-4,7-8,10"
    getRanges([4,7,10]) // "4,7,10"
    getRanges([2, 3, 8, 9]) // "2-3,8-9"
    

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

    function getRanges(arr: number[]) {
      return arr.map((v, k) => {
        if (v - 1 === arr[k - 1]) {
          if (arr[k + 1] === v + 1) {
            return ''
          } else {
            return `-${v},`
          }
        } else {
          return v + ','
        }
      }).join('').split(',-').join('-')
    }
    

    Из минусов: обращение к массиву по несуществующему индексу и некрасивая манипуляция со строкой: join-split-join. А ещё это решение неправильное, потому что на примере getRanges([1, 2, 3, 5, 6, 8]) возвращается «1-3,5-6,8,», и чтобы «убить» запятую в конце, нужно ещё нарастить условия, усложнив логику и снизив читаемость.

    Вот решение в духе Яндекса:
    const getRanges = arr => arr
      .reduceRight((r, e) => r.length ? (r[0][0] === e + 1 ? r[0].unshift(e) : r.unshift([e])) && r : [[e]], [])
      .map(a => a.join('-')).join(',')
    

    Поможет ли гугление писать такие элегантные решения? Чтобы выдать такой код, нужны две составляющие: опыт работы с множеством алгоритмов и отличное знание языка. И именно об этом предупреждают рекрутёры Яндекса: вас будут спрашивать об алгоритмах и языке. Яндекс предпочитает нанимать разрабов, способных писать крутой код. Такие прогеры эффективны, но, самое главное, взаимозаменяемы: они будут писать примерно одинаковые решения. Менее подкованные в теоретическом плане разработчики на одну задачу способны выдать десятки разнообразных, порой просто удивительных решений: один из кандидатов на нашу вакансию завернул такой костыль, что у меня глаза на лоб полезли.

    UPD: как заметил пользователь MaxVetrov, моё решение неверное:

    getRanges([1,2,3,4,6,7]) // 1-2-3-4,6-7

    Таким образом, я сам не смог нормально решить эту задачу до сих пор.

    UPD2: В целом, комментарии убедили меня, что этот код получился хреновым, даже если бы и работал правильно.

    Я не зря потратил время, переводя бумагу в офисе Яндекса, потому что за этим занятием я понял, как самому стать эффективным интервьюером. Я взял за основу идею и формат их задач, но:

    • Не предлагал писать код на бумаге, но просил писать в code.yandex-team.ru. Это многопользовательский онлайн-редактор кода без возможности его выполнения. Возможно, есть и другой, более удобный вариант, но искать и было, и осталось лень;
    • Не требовал идеального решения, но просил решить хоть как-то;
    • Не требовал знания языка наизусть, нужную функцию или метод можно было спросить;
    • Сократил время на техническое интервью до 30 минут.

    Одна из задач нашего технического интервью:

    let n = 0
    while (++n < 5) {
    	setTimeout(() => console.log(n), 10 + n)
    }
    // Что будет выведено в консоль?
    

    Я считаю, что для JavaScript-разработчика это очень показательный тест. И дело здесь не в замыкании и не в понимании отличий между преинкрементом и постинкрементом, а в том, что по какой-то необъяснимой причине четверть интервьюируемых полагает, что console.log выполнится раньше, чем завершится цикл. Я не преувеличиваю. У этих людей в резюме и опыт работы минимум два года, да и другие задачи, не завязанные на коллбеки, они успешно решали. То ли это новое поколение JavaScript-разработчиков, выросшее на async/await, которое ещё что-то слышало про Promise, но коллбеки для них — как дисковый телефон для современного подростка — номер наберёт, пусть не с первого раза, но так и не поймёт, как оно работает и зачем.

    Эта задача имеет продолжение: нужно дописать код так, чтобы console.log так же выполнялся внутри setTimeout, но в консоль вывелись значения 1, 2, 3, 4. Тут уместна поговорка «век живи — век учись», так как однажды один из интервьюируемых предложил такое решение:

    setTimeout(n => console.log(n), 10 + n, n)
    

    И тут я узнал, что setTimeout и setInterval третий и последующие аргументы передают в коллбэк. Стыдно, да. Знание, кстати, оказалось полезным: я не раз использовал эту особенность.

    А вот эту задачу я позаимствовал у Яндекса как есть:

    /* Необходимо реализовать функцию fetchUrl, которая будет использоваться следующим образом. Внутри fetchUrl можно использовать условный метод fetch, который просто возвращает Promise с содержимым страницы или вызывает reject */
    fetchUrl('https://google/com')
      .then(...)
      .catch(...) // сatch должен сработать только после 5 неудачных попыток получить содержимое страницы внутри fetchUrl
    

    Тут проверяются навыки работы с Promise. Обычно я прошу решить эту задачу на чистых промисах, а затем с использованием async/await. С async/await решение интуитивно простое:

    function async fetchUrl(url) {
      for (let n = 0; n < 5; n++) {
        try {
          return await fetch(url)
        } catch (err) { }
      }
      throw new Error('Fetch failed after 5 attempts')
    }
    

    К этому решению тоже можно применить поговорку «век живи — век учись», но уже относительно моего интервьюера из Яндекса: он не уточнил, что можно/нельзя использовать async/await, и когда я написал такое решение, он удивился: «я не работал с async/await, не думал что это можно решить так просто». Наверное, он ожидал увидеть что-то такое:

    function fetchUrl(url, attempt = 5) {
      return Promise.resolve()
        .then(() => fetch(url))
        .catch(() => attempt-- ? fetchUrl(url, attempt) : Promise.reject('Fetch failed after 5 attempts'))
    }'error'
    

    Этот пример способен показать насколько хорошо человек понимает промисы, это особенно важно на бэке. Однажды я видел код JavaScript разработчика, не разобравшегося до конца с промисами, подготовившего промис для транзакции sequelize следующим образом:

    const transaction = Promise.resolve()
    for (const user of users) {
      transaction.then(() => {
        return some_action...
      })
    }
    

    И удивлявшийся, почему в его транзакции фигурирует только один пользователь. Можно было бы использовать Promise.all, ну а можно было знать, что Promise.prototype.then не добавляет ещё один коллбэк, а создаёт новый промис и правильно будет так:

    let transaction = Promise.resolve()
    for (const user of users) {
      transaction = transaction.then(() => {
        await perform_some_operation...
        return some_action...
      })
    }
    

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

    public async addTicket(data: IAddTicketData): Promise<number> {
      const user = data.fromEmail ? await this.getUserByEmail(data.fromEmail) : undefined
      let category = data.category
    
      if (category === 'INCIDENT' && await this.isCategorizableType(data.type)) {
        category = 'INC_RFC'
      }
      const xml = await this.twig.render('Assyst/Views/add.twig', {
        from: data.fromEmail,
        text: data.text,
        category,
        user,
      })
      const response = await this.query('events', 'post', xml)
    
      return new Promise((resolve, reject) => {
        xml2js.parseString(response, (err, result) => {
          if (err) {
            return reject(new Error(err.message))
          }
          if (result.exception) {
            return reject(new Error(result.exception.message))
          }
          resolve(result.event.id - 5000000)
        })
      })
    }
    

    И попросил избавиться от ключевых слов async/await. С тех пор это задание стало первым и, в половине случаев, последним на собеседовании — его реально часто заваливают.

    Сам я ни разу не решал эту задачу до написания этой заметки и делаю это в первый в третий раз (первый слишком долгий, а во втором я не заметил один оставшийся await):



    Какой вывод из этого всего можно сделать? Собеседования — это интересно и полезно… Разумеется, если вы в срочном порядке не ищeте работу.

    Напоследок приведу ещё одну задачу из истории с Яндексом, я её ещё никому* почти не показывал, берёг, что называется, для особого случая. Есть набор баннеров, у каждого баннера есть «вес», который указывает на то, с какой частотой будет отображаться баннер относительно других баннеров:

    const banners = [
      { name: 'banner 1', weight: 1 },
      { name: 'banner 2', weight: 1 },
      { name: 'banner 3', weight: 1 },
      { name: 'banner 4', weight: 1 },
      { name: 'banner 5', weight: 3 },
      { name: 'banner 6', weight: 2 },
      { name: 'banner 7', weight: 2 },
      { name: 'banner 8', weight: 2 },
      { name: 'banner 9', weight: 4 },
      { name: 'banner 10', weight: 1 },
    ]
    

    Например, если есть три баннера с весами 1, 1, 2, их совокупный вес равен 4, а вес третьего равен 2/4 от общего веса, значит и отображаться он должен в 50% случаев. Необходимо реализовать функцию getBanner, которая рандомно, но с учётом весов, возвращает один баннер для показа. Решение можно проверить в этом сниппете, там же выводится ожидаемое и фактическое распределение.

    UPD: мне начали не только минусить саму статью, но и семимильными шагами жечь карму и я сперепугу скрыл материал, что некрасиво по отношению к комментаторам. Исправляю этот мудачизм с моей стороны.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 120

      +36
      Вот решение в духе Яндекса:
      const getRanges = arr => arr
      .reduceRight((r, e) => r.length? (r[0][0] === e + 1? r[0].unshift(e): r.unshift([e])) && r: [[e]], [])
      .map(a => a.join('-')).join(',')

      Поможет ли гугление писать такие элегантные решения?


      Это конечно вкусовщина, но такие решения как минимум спорные. Я не имею ничего против reduce/fold/назовите как хотите, но используемая внутри функция свертки должна быть выражена более ясно. А тут тернарный оператор на тернарном операторе сидит, и тернарным оператором погоняет.
        –42

        Чёт я не понял, это правда недостаточно ясное выражение?


        r.length ? (r[0][0] === e + 1? r[0].unshift(e) : r.unshift([e])) && r : [[e]]
          +60
          Оно абсолютно нечитаемо. Это практически обфусцированный код.
            +18

            На мой взгляд, совершенно неясное. Над ним надо сидеть, переформатировать и медитировать несколько минут.


            Для теста наверное ok, в production я бы такое завернул, как нечитаемое.

              –2
              этот вариант точно не хуже вереницы if/foreach,
              по мне это ясное выражение
                +9
                Правда. Мало того что нечитаемо, еще и магические числа во все поля. Что такое r[0][0]?
                  –1
                  Здесь нет никаких магических чисел, r[0][0] это первый элемент первого массива результата предыдущих применений этого коллбэка. Это довольно очевидно.

                  Или вы предлагаете ввести константу FIRST_ARRAY_ELEMENT?
                    +3

                    Например, присвоить это значение переменной с понятным именем

                      0
                      Наверно все таки не первый элемент, а нулевой.
                      Но все равно невозможно сходу понять что такое " первый элемент первого массива результата предыдущих применений этого коллбэка". При том что в этом куске кода нет никаких коллбэков и асинхронщины.
                      А вообще я бы предлагал не обфусировать код на ровном месте, пусть он будет и не в «стиле яндекса». Но его хотя бы поправить можно будет в случае ошибки или изменения требований.
                        0
                        Какая асинхронщина? Вы о чем?

                        Этот «кусок кода» — содержимое функции передаваемой в reduceRight. Функция, передаваемая в другую функцию для последующего вызова называется callback, если вы не в курсе.

                        И да, я также считаю, что код трудночитаемый, но совсем не из-за r[0][0], а из-за вложенного тренарника и использования && для ветвления; вложенные условия человеку всегда тяжело анализировать, особенно если записать их в одно выражение. В добавок первое условие r.length (или, если более явно r.length != 0) выполняется только в первый раз, и можно было передать этот элемент вторым аргументом в reduceRight, а из массива его исключить. Когда читаешь код, обычно пытаешься мысленно его выполнить, а лишние условия только сбивают с толку.
                          –1
                          Коллбэк это по определению функция обратного вызова. В жаваскрипте выполняются асинхронно.
                          Лямбда, передаваемая в reduceRight не является коллбэком. Это не функция обратного вызова.
                          Обычно каша в голове и рождает как раз подобную кашу в коде.
                            0

                            В документации первый аргумент reduceRight называется не иначе как callback: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight

                              0
                              Очень жаль, что у вас такая каша в голове. Коллбэк не имеет никакого отношения к асинхронщине.

                              Даже педивики говорит:
                              Callback (англ. call — вызов, англ. back — обратный) или фу́нкция обра́тного вы́зова в программировании — передача исполняемого кода в качестве одного из параметров другого кода.


                              Что мы делаем? Передаем исполняемый код (эту лямбду) в качестве параметра в другой код (в reduceRight). Подходит под определение выше?
                            0
                            Наверно все таки не первый элемент, а нулевой.

                            Наверно все-таки первый элемент, имеющий нулевой индекс.
                        0
                        Его назначение не следует из формы.
                          +5

                          Да, правда.

                            +10
                            По такому коду сразу видно «командного игрока».
                              +2
                              Я, конечно, JavaScript-ер, и не боюсь компактного кода, но идея использовать в выражениях методы с побочкой — на мой взгляд, так себе.
                              +20
                              Согласен. Оно трудночитаемо и сходу непонятно, что хотел сказать автор. Особенно весело такое дебажить. Иногда лучше не экономить, а честно написать императивный код. Работать будет также, а читать в сто раз проще (верно и обратное, но для других случаев).
                                +1

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

                                  0
                                  >скорее всего это будет императивный код в стиле си
                                  Я начинал с Алгола-60, так что си вряд ли ;) Но в целом мысль здравая, хотябы потому, что какой-то один стиль надо же выбрать, даже если знаешь и умеешь в разных.
                                    0

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

                                0

                                Последняя задача на знание математики, нет?
                                https://en.wikipedia.org/wiki/Cumulative%5Fdistribution%5Ffunction

                                  0
                                  При большом количестве прогонов (>= тому, что в тесте) обычный random там дает первую-вторую цифру после запятой. Так что видимо да, надо подправлять его на ходу.
                                  +8
                                  Чем то напоминает микроменеджмент, если честно.
                                  Вы считаете что замена 10 строк с foreach и if на 5 строк с array map/reduce и тернарниками дадут какой-то значимый профит качеству крупного проекта и невозможны для понимания тем кто привык к первому варианту?
                                    –21

                                    Я полагаю, что тенденция к написанию 50 строк там, где можно обойтись 25 строками, в перспективе действительно способна навредить проекту.

                                      +23
                                      Зависит от читаемости строк. Это ж не one-line-code challenge. Коротко хорошо только тогда, когда оно также легко читается.
                                        +5
                                        1.
                                        Я полагаю, что тенденция к написанию 50 строк там, где можно обойтись 25 строками, в перспективе действительно способна навредить проекту.

                                        Мне кажется если у вас в одной функции 25 строк такого месива из тернарных операторов(или if`ов), уже что-то не так с декомпозицией и качеством кода, а цикломатическая сложность функции где-то за пределами досягаемости.

                                        Распространение знаний в команде через код ревью или парное программирование не решают проблему? Чтобы заменить foreach на array_map нужен какой-то серьёзный теоретический и математический бэкграунд?

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

                                        Мне казалось что для проверки умения решить практические задачи следует спрашивать про практические задачи и примеры брать из них же, а «Необходимо реализовать функцию getRanges, которая возвращает следующие результаты» слабо напоминает таковую, и слабо корреллирует с упомянутым вами «умением хорошо решать задачи».

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

                                        Пока что остаётся ощущение что про map/reduce спрашивается потому что интервьюеру так понравилось и он решил непременно блеснуть своими умениями перед глупыми кандидатами.
                                          +6
                                          Код не должен легко писаться, код должен легко читаться (с) Не мое.
                                            +2
                                            Лишь то читается легко,
                                            Что написано с трудом.
                                            Что в час написано,
                                            То в час и позабыто.

                                            А.С., он самый, наш, Пушкин
                                            +6
                                            Вообще-то, полезность скажем fold/reduce состоит в том, что оно более явно, чем циклы, выражает намерение автора. Т.е. мы берем массив, и схлопываем его при помощи функции. Но в данном случае этого не происходит, в частности потому, что ваша функция свертки довольно таки нетривиальная, и ее собственные намерения вообще не ясны, пока не вчитаешься, и не поймешь назначение верхнего уровня, так сказать.

                                            Ну то есть, в целом вы скорее правы, чем нет, но конкретный пример — ну так себе.
                                          +1
                                          — Не предлагал писать код на бумаге, но просил писать в code.yandex-team.ru. Это многопользовательский онлайн-редактор кода без возможности его выполнения. Возможно, есть и другой, боле удобный вариант, но искать и было, и осталось лень;

                                          Есть еще colabedit, когда я пытался пройти собеседование в Яндекс(весна 2017) — использовали его.
                                            0
                                            Если чисто для JS, то премиум версия Сodepen фаворит без вариантов.
                                              0
                                              А кто именно в данном случае должен платить за CodePen? Все пользователи или только создатель комнаты?
                                            +17
                                            Какой кошмар. Спасибо, что пролили свет на интервью в Яндексе.

                                            В частности, за решение задач «в духе Яндекса». Люблю и уважаю ФП, но однобуквенные переменные, тернарка в тернарке это за гранью. Можно ведь сделать проще.

                                            К тому же, используется Array.prototype.unshift, что, как бы, «неправославно» в рамках функциональной парадигмы, которой вы пытаетесь следовать.

                                            Ваше решение с промисами (там, где gif), так же под вопросом. Во первых, переменная user там не нужна. category можно оставить как шорткат к data.category. Первые два запроса можно выполнить паралельно, что уменьшит время ответа.

                                            public addTicket(data: IAddTicketData): Promise<number> {
                                              const fromEmail = data.fromEmail;
                                              const category = data.category;
                                            
                                              return Promise.all([
                                                fromEmail ? this.getUserByEmail(fromEmail) : undefined,
                                                category === 'INCIDENT' && this.isCategorizableType(data.type),
                                              ])
                                              .then(([user, isCategorizable]) => this.twig.render('Assyst/Views/add.twig', {
                                                from: fromEmail,
                                                text: data.text,
                                                category: isCategorizable ? 'INC_RFC' : category,
                                                user,
                                              }))
                                              .then(xml => this.query('events', 'post', xml))
                                              .then(response => new Promise((resolve, reject) => {
                                                xml2js.parseString(response, (err, result) => {
                                                  const error = err || result.exception;
                                            
                                                  if (error) {
                                                    reject(error);
                                                  } else {
                                                    resolve(result.event.id - 5000000);
                                                  }
                                                });
                                              }));
                                            }
                                            
                                              +9
                                              У меня почему-то ваше решение в браузере такой ответ выдает:
                                              let a = [1,2,3,4,6,7];
                                              getRanges(a);
                                              «1-2-3-4,6-7»
                                              Ошибочка.
                                                +15
                                                Потому что надо
                                                const getRanges = arr => arr
                                                .reduceRight((r, e) => r.length ? (r[0][0] === e + 1 ? r[0].unshift(e): r.unshift([e])) && r: [[e]], [])
                                                .map(a => `${a[0]}-${a[a.length-1]}`).join(',')

                                                Но это очень хорошо показывает всю ценность такого стиля написания кода, да. Автор статьи топит за крутость, качество, и единообразие решений, и постит в статью ошибочный код, потому что тупо не видит, что он ошибочный — да и едва ли это кто-то «на глазок» увидит, кроме совсем уж упоровшихся.
                                                  +2
                                                  Так лучше, но тест [4,7,10] не проходит.
                                                    +5
                                                    Хорошо-хорошо, пусть будет так
                                                    const getRanges = arr => arr
                                                    .reduceRight((r, e) => r.length ? (r[0][0] === e + 1 ? r[0].unshift(e): r.unshift([e])) && r: [[e]], [])
                                                    .map(a => a[0] + (a.length > 1 ? '-'+a[a.length - 1]: '')).join(',')

                                                    Надеюсь, достаточно нечитабельно написал, чтоб соответствовать.
                                                      +3
                                                      Вот так не работает:
                                                      [1,2,3,3,6,7,10,11]
                                                      и так.)
                                                      [7,1,2,3,6,7,10,11].

                                                      PS. Cколько ж покосили наверно людей на этом )
                                                      Лучше свой пэт-проект делать и показывать, чем такие тестовые задания проходить.
                                                        +2
                                                        Так и не будет — из того, что про постановку задачи видно, это невалидные входные данные. Валидные — отсортированные по возрастанию натуральные числа без повторов.
                                                    +4
                                                    И это тоже неправильный вариант :)

                                                    getRanges([1,2,3,6]) => "1-3,6-6"

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

                                                    На самом деле, есть и неплохие (но неполные) упражнения (в частности, игры со счётчиками и setTimeout, которые показывают понимание Event Loop и области видимости), но мне откровенно жаль, что 30+ человек попали к некомпетентному интервьюверу и я не понимаю, как крупная продуктовая компания могла такое допустить.
                                                      +1
                                                      Пример с setTimeout — это «проверка нулевого уровня», нечто такое, что позволяет отфильтровать людей, которые и правда что-то делали от людей, которые setTimeout разве что в книжках видели, да и то мельком. В этом смысле это нормальное упражнение, лично я считаю, что как минимум одно такое (а лучше побольше) должно быть на каждом собеседовании как самый очевидный тест для отсева самозванцев.
                                                        0
                                                        Хорошая тема для холиваров: нужно ли преподавать basic в школах?
                                                        Option Explicit
                                                        
                                                        Function GetRanges(ByRef arr() As Variant, Optional _
                                                          ByVal hyphen As String = "-") As String
                                                          Dim span(1 To 3) As Variant, element As Variant
                                                          
                                                          If Not (Not Not arr) > 0 Then Exit Function
                                                          
                                                          For Each element In arr
                                                            If element - span(3) > 1 Then
                                                              SpanSeparation GetRanges, span
                                                            ElseIf element - span(3) = 1 And IsEmpty(span(1)) Then
                                                              span(1) = span(3)
                                                              span(2) = hyphen
                                                            End If
                                                            span(3) = element
                                                          Next element
                                                          
                                                          SpanSeparation GetRanges, span
                                                        End Function
                                                        
                                                        Sub SpanSeparation(ByRef band As String, ByRef span() As Variant)
                                                          If Len(band) > 0 Then band = band & ","
                                                          band = band & Join(span, "")
                                                          Erase span
                                                        End Sub

                                                          0
                                                          А я бы постарался обойтись без вариантов.
                                                    +2
                                                    Теперь понятно, почему джуны никому не нужны, они тупо не владеют скиллами, чтобы влезть в тусовку сеньоров, а скиллы можно получить только от сеньоров.
                                                      0
                                                      Но вот с тезисом о практической бесполезности яндексовских заданий я не соглашусь. Даже рутинная разработка нет-нет, да поставит перед тобой задачу, у которой есть несколько решений.
                                                      Может быть, я тупой, объясните пожалуйста, где и зачем на практике может пригодится функция getRanges() в первом примере? И чем плохо решить ее «в лоб» (через for/forEach + if)?
                                                        0
                                                        Задачка превратить набор точек или набор пересекающихся отрезков в набор непересекающихся отрезков попадается на практике. Мне она периодически встречается в работе, например при анализе каких-нить данных, чтобы понять, где дырки.
                                                          0
                                                          Второй вопрос забыли. Потому что да, самое эффективное решение тут — тупо в лоб однократным проходом. А хипстерский yandex-way из статьи ковыряется намного больше.
                                                            0
                                                            По второму вопросу я возражений не имею, отметил лишь, что задачка не с потолка.
                                                            0
                                                            понял, спасибо. Если несложно, хотел бы увидеть какую-то конкретную задачу. Если несложно. Я не придираюсь, действительно интересно. Я в области анализа данных не силен вообще, было бы интересно.
                                                              +1
                                                              Можно например поискать интервалы простоя чего-нибудь. Процессора или билд-агента. Можно вычислять свободное время группы людей, имея их расписания с пересекающимися занятыми интервалами и т.п.
                                                                0
                                                                спасибо)
                                                          +10
                                                          Одна из задач нашего технического интервью:

                                                          let n = 0
                                                          while (++n < 5) {
                                                          setTimeout(() => console.log(n), 10 + n)
                                                          }
                                                          // Что будет выведено в консоль?


                                                          Дальше читать не стал :)
                                                          Правильный ответ на такую задачу — «ничего», не надо так писать.
                                                            0
                                                            Да, что касается стиля — я тоже неоднократно проходил подобные задачи в рамках какого-либо тестирования, и честно скажу, что в письменном тесте подобное раздражает неимоверно, потому что к практике никакого отношения почти не имеет. И писать так действительно не нужно.

                                                            В общем, задача конечно та еще, но с другой стороны, а вы бы что предложили?

                                                            На мой взгляд, это задачка на понимание концепции. То есть, заданная как повод для рассуждений на тему, что и в каком порядке будет в таком коде происходить — почему бы и нет? В устном общении, и как тема для обсуждения.

                                                            Ну и кстати — такой ответ как у вас, я бы вполне принял, но дальше бы попросил объяснить, почему так не стоит.
                                                              +4
                                                              Такой код вполне допустим на сосебедовании, но я для него поставил бы совершенно другие вопросы кандидату. Но я не показатель, я считаю что простое решение это зачастую хорошее решение, что что-то непонятное это или просто что-то ненужное, или это секта или это маркетинг.

                                                              Вопросы:
                                                              1. Что в нём плохо?
                                                              2. Что в нём хорошо?
                                                              3. Объясните что он делает

                                                              Вопрос №1 очень важен по любой теме, ответ на него даст вам понять кто перед вами, профессионал или не совсем, профессионал всегда знает минусы и ограничения того в чём он профессионал.
                                                                +1
                                                                >я для него поставил бы совершенно другие вопросы
                                                                Ну да, примерно так. Не требовать точный (и ненужный) ответ, а попросить рассказать про этот фрагмент.
                                                            +9

                                                            Мне не нравятся оба приведенных решения для getRanges — по моему мнению, они оба нечитаемая лапша, так как с первого взгляда трудно понять логику выполнения кода. В них идет злоупотребление функциями вроде map(), которые тут не нужны и служат лишь цели показать знание ES6, хотя в задаче этого не требуется. Вместо того, чтобы сделать решение самым простым способом, вы пишете код, который надо долго разбирать, который делает какие-то хитрые преобразования и на который будут плеваться все, кому придется его читать.


                                                            Если в Яндексе или у вас так пишут, то я сочувстсвую вашим разработчикам.


                                                            Я хочу сделать другое решение — максимально простое и последовательное, которое будет легко прочесть и понять другим разработчикам. И которое не будет демонстрировать знание функций вроде map/reduce, хотя я их знаю, хотя я и не помню список аргументов reduce без заглядывания в MDN, хотя я не вижу тут проблемы. Логика функции проста:


                                                            • сортировка значений по возрастанию
                                                            • группировка в массив диапазонов
                                                            • форматирование и склеивание диапазонов

                                                            У меня получается такой код, я использую ES3 для макс. совместимости, так как в задаче ничего про версию языка не сказано.


                                                            /**
                                                             * Принимает на вход массив значений вида [1, 2, 3, 5, 7, 8] и возвращает
                                                             * массив диапазонов вроде "1-3,5,7-8".
                                                             *
                                                             * @param {int[]} values
                                                             * @return {string}
                                                             */
                                                            function getRanges(values) {
                                                            
                                                                // Массив диапазонов вида "2-5" или "7"
                                                                var ranges = [];
                                                                function addRange(start, end) {
                                                                    if (end == start) {
                                                                        ranges.push(start);
                                                                    } else {
                                                                        ranges.push(start + "-" + end);
                                                                    }
                                                                }
                                                            
                                                                // Делаем копию массива и сортируем ее
                                                                values = values.slice();
                                                                values.sort();
                                                                if (!values.length) {
                                                                    return "";
                                                                }
                                                            
                                                                var start = values[0], end = values[0];
                                                            
                                                                for (var i = 1; i < values.length; i++) {
                                                                    var value = values[i];
                                                                    if (value == end - 1) {
                                                                        end = value;
                                                                        continue;
                                                                    }
                                                            
                                                                    addRange(start, end);       
                                                                    start = end = value;            
                                                                }
                                                            
                                                                // Добавляем последний диапазон, который мы не добавили внутри цикла
                                                                addRange(start, end);    
                                                                return ranges.join(",");
                                                            }
                                                            

                                                            Ну и раз уж речь зашла про задания с собеседований, вот задача, которая мне нравится, хотя возможно, она и не очень полезная. Объясните, почему в некоторых реализациях JS (например, в Firefox и Chrome) следующий код выдает такие результаты:


                                                            0.1 + 0.1 === 0.2  // true
                                                            0.1 + 0.1 + 0.1 === 0.3  // false
                                                            0.1 + 0.1 + 0.1 + 0.1 === 0.4 // true

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

                                                              +17
                                                              Если вы смогли объяснить, то расскажите, каким математическим условиям должны соответствовать числа, чтобы их можно было представить в этих реализациях JS точно и без ошибок. Если не можете, то идите учить теорию, а не по собеседованиям ходите.

                                                              Вам точно нужен тот, кто заучил всю основную проблематику IEEE 754 на память? Или это такой же обыденный выпендрёж, как и у автора статьи в духе «мне это нравится, значит все, кого я собеседую, должны в это мочь»?
                                                                0
                                                                >расскажите, каким математическим условиям должны соответствовать числа
                                                                Вы хотите подвести какую-то глубокую теорию под то, что в двоичной системе не все десятичные числа точно представимы?
                                                                  –1

                                                                  Я вижу, вы знаете особенности двоичной системы. Тогда вам наверно не составит труда объяснить, какие из этих чисел: 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6 точно представимы и почему.

                                                                    +1
                                                                    Вообще я тут не для того, чтобы интервьюироваться ) Я скорее хотел уточнить предыдущий вопрос — вы когда такое спрашиваете, вы хотите понимания основ, или точного знания деталей? Ну то есть, если бы меня такое спросили, я бы ответил, что в двоичной системе некоторые десятичные дроби представляются как периодические, а если бы я спрашивал — то меня бы такой ответ устроил, потому что если человек это знает — то остальное он уже может найти в гугле или учебнике. Ну это типа как исторический анекдот про ЕМНИП, Энштейна и Эдисона.
                                                                      0

                                                                      Спасибо, я вам перезвоню, хотя у меня нет вашего номера телефона и я не стану звонить незнакомому человеку просто так.


                                                                      Если вы знаете представление числа в IEEE754, вы без труда ответите на вопрос или самостоятельно додумаетесь. Я тоже знаю про периодические дроби, но хотел узнать, способен ли собеседуемый разобраться, какие именно дроби будут периодическими. Я такую задачу ни у кого не спрашивал, так как кандидаты не могли нормально решить и более простые, но решил запостить на Хабр, может кто сможет решить или кому будет интересно поломать голову. При этом добавил немного провокационную концовку про обучение, в надежде подогреть интерес.


                                                                      Задача, как мы видим, проверяет понимание IEEE754, знает ли человек эту систему представления чисел или лишь что-то краем уха слышал.


                                                                      И должен уточнить свою задачу, выше я писал "в некоторых реализациях JS", но я тут глянул стандарт ECMA-262 и там недвусмысленно сказано, что во всех реализациях JS должна использоваться IEEE754.


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

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

                                                                        Если ваши задачи состоят в том, что кто-то спрашивает, какие числа в IEEE 754 будут периодическими, а вы должны максимально быстро и полно ответить — тогда ваш тезис безусловно верен. Вопрос только в том, действительно ли у вас такие задачи.
                                                                          0

                                                                          Я много думал над тем, какие задачи надо придумывать. Они должны быть лаконичными в формулировке, понятными на слух. Реальные рабочие задачи давать трудно, так как чтобы их понять, надо знать особенности разрабатываемого ПО, а это быстро не рассказать.


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

                                                                            +3
                                                                            Реальные рабочие задачи давать трудно, так как чтобы их понять, надо знать особенности разрабатываемого ПО, а это быстро не рассказать.

                                                                            То есть, вы спрашиваете у людей какие-то вопросы, в равной степени не относящиеся к вашим реальным задачам, и на основании их ответов определяете, насколько они хорошо будут работать над реальными задачами?

                                                                            Скажите, а коэффициент корелляции вы уже посчитали, или это всё проходит по разряду «я так вижу»?
                                                                          +1
                                                                          >Если рассуждать «остальное человек может найти», то нет смысла проводить собеседования.
                                                                          Э, ну как сказать. Я вообще не знаю, что такое IEEE754, но при этом понимаю, что двоичное представление отличается от десятичного. И имею периодический же опыт JS начиная с 1996 года. Ну т.е. более 20 лет, плюс-минус. И на подобные вопросы я как правило отвечаю на собеседованиях примерно «как Энштейн» — это можно посмотреть в справочнике (и обычно я знаю, в каком именно).

                                                                          Ну то есть, я ничего плохого не хочу сказать про ваши методы собеседования, так как не знаю, кто и зачем вам нужен. Просто при таком подходе вы очевидно потеряете часть адекватных людей. Опять же — вы кого-то потеряете почти при любом подходе, если вы не гугль, и к вам не стоит длинная очередь отличных кандидатов. Это не недостаток метода — это его ограничение.
                                                                        0

                                                                        Очевидно же, что при правильно выбранном представлении точно представимы все.

                                                                          0
                                                                          Зачем Вы выходите из контекста? Речь в этой ветке идёт сугубо об IEEE754
                                                                            0

                                                                            Затем, что контекст — штука плавающая, и ее полезно задавать явно.

                                                                              0
                                                                              Ну, знаете, «в некоторых реализациях JS», как мне кажется, достаточно хорошо указывает обсуждаемый формат представления дробных чисел.
                                                                      0
                                                                      Ваша функция работает неверно
                                                                        0
                                                                        Уже занёс руку поставить плюсик, мол, вот адекватный интевьюер. А потом — опа! — те же таймауты, только сбоку
                                                                        –2
                                                                        Скажите, а почему на беке именно важны промисы. Вот прям так, что бы на них писать код.

                                                                        Я к тому, что NodeJS давно поддерживает async/await. А для более старых версий, код всегда можно прогнать через babel и получить поддержку. А если у вас typescript, так в конфиге указали, какая поддержка ES вам нужна, и радуетесь.

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

                                                                        PS Я знаю как работают промисы, даже когда-то делал полифил для IE, что бы получить поддержку в ослике. Но когда появился async/await это же как глоток свежего воздуха.
                                                                          +2

                                                                          Как вы собрались пользоваться async/await, не понимая промисов? Это ведь конструкции для работы с ними. Как вы сделаете ожидание разрешения 3 промисов, как вы поймете, корректно ли обрабатывается отказ одного из них?

                                                                            0
                                                                            Я где-то выше написал, что непонимаю промисов и как они работают? Или я не знаю, что asyn/await это всего лишь сахар?

                                                                            Еще раз повторю — код на промисах и код на async/await — это два разных вида написания кода. И второй вид, более простой и читабельный. Если человек написал выполнение кода с async/await, с моей точки зрения это только плюс.
                                                                          +13
                                                                          способных писать крутой код

                                                                          Код, конечно, крутой, вот только он в 10 раз медленнее, если писать «в лоб» с циклами
                                                                          (6711 мсек против 463 мсек)
                                                                          <script>
                                                                          
                                                                          function generateTest()
                                                                          {
                                                                            var test = [];
                                                                            for (var i = 0; i < 10000000; i++)
                                                                              if (Math.random() < 0.8)
                                                                                test.push(i);
                                                                            return test;
                                                                          }
                                                                          
                                                                          const getRanges = arr => arr
                                                                            .reduceRight((r, e) => r.length ? (r[0][0] === e + 1 ? r[0].unshift(e): r.unshift([e])) && r: [[e]], [])
                                                                            .map(a => a[0] + (a.length > 1 ? '-'+a[a.length - 1]: '')).join(',')
                                                                          
                                                                          
                                                                          function getRanges2(arr)
                                                                          {
                                                                            var ranges = [];
                                                                            var start = 0;
                                                                            var size = arr.length;
                                                                            while (start < size)
                                                                              for (var i = start + 1; i <= size; i++)
                                                                              {
                                                                                if (i == size || arr[i] !== arr[i - 1] + 1)
                                                                                {
                                                                                  ranges.push(i == start + 1 ? '' + arr[i - 1] : '' + arr[start] + '-' + arr[i - 1]);
                                                                                  start = i;
                                                                                  break;
                                                                                }
                                                                              }
                                                                          
                                                                            return ranges.join(',');
                                                                          }
                                                                          
                                                                          
                                                                          window.onload = function ()
                                                                          {
                                                                            var a = generateTest();
                                                                          
                                                                            var t0 = performance.now()
                                                                            var tmp1 = getRanges(a);
                                                                            var t1 = performance.now()
                                                                            var tmp2 = getRanges2(a);
                                                                            var t2 = performance.now()
                                                                          
                                                                            alert('' + (t1 - t0) + ', ' + (t2 - t1) + '');
                                                                          
                                                                            if (tmp1 !== tmp2)
                                                                              alert('error');
                                                                          }
                                                                          
                                                                          </script>
                                                                            +1
                                                                            Promise.resolve().then(() => fetch(url))


                                                                            Вместо этого можно просто написать fetch(url), нет?
                                                                              +1
                                                                              Все ошибаются, не судите строго.
                                                                              В задаче не указано, что должно быть на выходе если массив не отсортирован, повторяются числа:
                                                                              getRanges([7, 0, 1, 0, 1, 2, 3, 4, 7, 8, 10])
                                                                              На выходе должно быть:
                                                                              а)«0-4,7-8,10»
                                                                              б)«0-1,0-4,7-8,10»
                                                                              в)«7,0-1,0-4,7-8,10»?
                                                                              Т.е. существует неопределенность, потому что нет контекста использования функции и\или не указаны пограничные случаи.
                                                                                0
                                                                                Подозреваю вопрос и предполагает развитие беседы в стиле уточнения граничных условий интервьюируемым, либо же дополнительные вопросы от интервьюера по развитию и доработке кода после решения задачи только для указанных изначально кейсов.
                                                                                +14
                                                                                300iq решение (не воспринимайте серьезно, просто шутка)
                                                                                /* Необходимо реализовать функцию getRanges, которая возвращает следующие результаты: */
                                                                                getRanges([0, 1, 2, 3, 4, 7, 8, 10]) // "0-4,7-8,10"
                                                                                getRanges([4,7,10]) // "4,7,10"
                                                                                getRanges([2, 3, 8, 9]) // "2-3,8-9"

                                                                                
                                                                                function getRanges(arr) {
                                                                                    if (arr.length === 3) return "4,7,10"
                                                                                    else if (arr.length === 4) return "2-3,8-9"
                                                                                    else if (arr.length === 8) return  "0-4,7-8,10"
                                                                                    return false;
                                                                                }
                                                                                
                                                                                  0

                                                                                  Заметим в скобках, даже можно методологическое обоснование подогнать.

                                                                                    0
                                                                                    Почему то вспомнил как где то читал что даже оверрайд хешкода на захардкоженное значение можно оправдать))
                                                                                    +1
                                                                                    решение неправильное. нужно было использовать switch
                                                                                      0
                                                                                      :DD
                                                                                      Кстати, не знал до этого, есть ли в js switch. С этим языком я почти незнаком. Знаю только что if есть везде
                                                                                        0
                                                                                        Знаю только что if есть везде
                                                                                        В Assembler — нет. Ну или в Brainfuck. :]
                                                                                          +1
                                                                                          От ассемблера зависит. Как директива препроцессора наверное почти везде есть. На многих их можно реализовать на макросах. В форт-ассемблерах почти всегда есть высокоуровневые конструкции из форта.
                                                                                            0
                                                                                            В ассемблере есть функции сравнения и условные переходы, чем в принципе, if и является
                                                                                                cmp al,5  ; if (a == 5)
                                                                                                jne label1
                                                                                                inc al    ; { a++; }
                                                                                            label1:
                                                                                            
                                                                                            
                                                                                        +4
                                                                                        function getRanges(arr) {
                                                                                            return ["0-4,7-8,10", "2-3,8-9", "4,7,10"][arr[0] >> 1]; 
                                                                                        }
                                                                                        
                                                                                          0

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

                                                                                          0
                                                                                          function fetchUrl(url, attempt = 5) {
                                                                                            return Promise.resolve()
                                                                                              .then(() => fetch(url))
                                                                                              .catch(() => attempt-- ? fetchUrl(url, attempt) : Promise.reject('Fetch failed after 5 attempts'))
                                                                                          }'error'
                                                                                          Разве не будет отправлено 6 запросов? Кажется, имелось ввиду:
                                                                                          --attempt
                                                                                          .
                                                                                            +3

                                                                                            Не удивительно, что Go, который почти как BASIC, так взлетел.

                                                                                              +2

                                                                                              Решал похожую задачу на Scala когда выполнял задачки с курса, там я решил сделать моноид ренжей:


                                                                                              object Monoid {
                                                                                                sealed trait Ordered // это АДТ в стиле scala, всего 3 варианта
                                                                                                case class Range(a: Int, b: Int) extends Ordered
                                                                                                case object Unordered extends Ordered
                                                                                                case object Mempty extends Ordered
                                                                                              
                                                                                                def orderMonoid: Monoid[Ordered] = new Monoid[Ordered] {
                                                                                                  def zero: Ordered = Mempty
                                                                                                  def op(a: Ordered, b: Ordered): Ordered = (a, b) match {
                                                                                                    case (Range(la, ha), Range(lb, hb)) if ha <= lb => Range(la, hb)
                                                                                                    case (Mempty, i) => i
                                                                                                    case (i, Mempty) => i
                                                                                                    case _ => Unordered
                                                                                                  }
                                                                                                }

                                                                                              Объявляем энум, нас ренж либо пустой, либо неупорядочен, либо упорядоченный ренж a-b.


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


                                                                                                def isOrdered(v: Seq[Int]): Boolean = {
                                                                                                  // превращаем каждый элемент списка в упорядоченный ренж из него же самого
                                                                                                  // а потом делаем свертку плюсом моноида
                                                                                                  foldMap(v, orderMonoid)(a => Range(a, a)) match {
                                                                                                    case Unordered => false
                                                                                                    case _ => true
                                                                                                  }
                                                                                                }

                                                                                              Вроде все достаточно очевидно, по аналоги можно решить и эту задачу.
                                                                                              А однострочник из нечитаемых мутирующих функций назвать читаемым, к сожалению, не могу, как и "ФПшным".




                                                                                              К слову, и формулировка задачи странная:


                                                                                              / Необходимо реализовать функцию getRanges, которая возвращает следующие результаты: /

                                                                                              Есть бесконечное множество функций которые будут выдавать такие результаты, вплоть до switch case из трех веток.

                                                                                                0

                                                                                                Продолжая играться с хаскеллем (уж очень он меня порадовал когда готовил последнюю статью), решил на нем сделать конкретно эту задачу:


                                                                                                getRanges :: [Int] -> [(Int, Int)]
                                                                                                getRanges = foldr go ([])  -- делаем reduce
                                                                                                  where
                                                                                                    go x t = case t of -- берем очередной элемент и аккумулятор
                                                                                                      ((l, r):as) | l - x == 1 -> ((x, r) : as) -- если там что-то есть и можем всунуть, то расширяем ренж текущим элементом
                                                                                                      _ -> (x, x) : t     -- иначе создаем новый интервал
                                                                                                
                                                                                                main :: IO ()
                                                                                                main = print $ getRanges [0, 1, 2, 3, 4, 7, 8, 10]

                                                                                                Достаточно аккуратно. Чтобы сделать кастомный принт нужно будет немного кода дописать:


                                                                                                main = do
                                                                                                  let ranges = getRanges [0, 1, 2, 3, 4, 7, 8, 10]
                                                                                                  let format = fmap $ \x -> case x of 
                                                                                                                (a, b) | a == b -> $"{a}"
                                                                                                                (a, b) -> $"{a}-{b}"
                                                                                                  print $ format ranges
                                                                                                  0
                                                                                                  Ну это все-таки немного неспортивно, в оригинале был ES, а там система типов слегка так слабее чем у скалы и хаскеля. Но как направление куда двигаться — почему бы и нет?
                                                                                                  0
                                                                                                  Мой костыль с ретузами:
                                                                                                  function getRanges6(arr) {
                                                                                                      let ret = arr.filter((value, index, self) => self.indexOf(value) === index);
                                                                                                      ret = ret.sort((a, b) => a > b);
                                                                                                      let left = ret[0];
                                                                                                      let str = "";
                                                                                                      let acc = ret.reduce(function(acc, value, index, array) {
                                                                                                          if (value != acc + 1) {
                                                                                                              str += left.toString();    
                                                                                                              if (left != acc) {
                                                                                                                  str += "-" + acc.toString();    
                                                                                                                  left = value;
                                                                                                              }
                                                                                                              str += ","
                                                                                                          }
                                                                                                          return value;
                                                                                                      });
                                                                                                  
                                                                                                      if (left != acc) {
                                                                                                          str += left.toString() + "-";    
                                                                                                      }
                                                                                                      str += acc.toString();    
                                                                                                      return str;
                                                                                                  }
                                                                                                  
                                                                                                    0
                                                                                                    Затесалась ошибка.)
                                                                                                    Исправлено
                                                                                                    function getRanges6(arr) {
                                                                                                        let ret = arr.filter((value, index, self) => self.indexOf(value) === index);
                                                                                                        ret = ret.sort((a, b) => a > b);
                                                                                                        let left = ret[0];
                                                                                                        let str = "";
                                                                                                        let acc = ret.reduce(function(acc, value, index, array) {
                                                                                                            if (value != acc + 1) {
                                                                                                                str += left.toString();    
                                                                                                                if (left != acc) {
                                                                                                                    str += "-" + acc.toString();
                                                                                                                }
                                                                                                                left = value;
                                                                                                                str += ","
                                                                                                            }
                                                                                                            return value;
                                                                                                        });
                                                                                                    
                                                                                                        if (left != acc) {
                                                                                                            str += left.toString() + "-";    
                                                                                                        }
                                                                                                        str += acc.toString();    
                                                                                                        return str;
                                                                                                    }

                                                                                                    0

                                                                                                    Чисто для популяризации Crystal — на нём реализация. Почти как Ruby, но не совсем.


                                                                                                    alias LR = Tuple(Int32, Int32)
                                                                                                    alias S = Tuple(Int32)
                                                                                                    
                                                                                                    def getRanges(a : Array(Int32))
                                                                                                      a.reduce([] of LR | S) { |total, cur|
                                                                                                        total << (total.pop?.try { |x|
                                                                                                          if cur - x.last <= 1
                                                                                                            { x.first }
                                                                                                          else
                                                                                                            total << x
                                                                                                            Tuple.new
                                                                                                          end
                                                                                                        } || Tuple.new) + { cur }
                                                                                                      }.map { |el| el.join('-') }.join(',')
                                                                                                    end
                                                                                                      0

                                                                                                      А еще проще там задач нет? Скучно.


                                                                                                      const getRanges = (numbers) => {
                                                                                                          const normalized = numbers.slice().sort((a, b) => a - b)
                                                                                                          let latest = null
                                                                                                          return normalized.reduce((ranges, n) => {
                                                                                                              const i = latest !== n - 1 ? ranges.push([]) : ranges.length
                                                                                                              latest = n
                                                                                                              ranges[i - 1].push(n)
                                                                                                              return ranges
                                                                                                          }, []).map((range) => {
                                                                                                              return range.length === 1 ? range[0] : `${range[0]}-${range[range.length - 1]}`
                                                                                                          }).toString()
                                                                                                      }

                                                                                                      Ну и чисто по фану, то же самое, только пожатое до мама не горюй ) Напишите кто-нибудь короче:


                                                                                                      const getRanges = (n,l)=>''+n.slice().sort((a,b)=>a-b).reduce((r,n)=>(r[(l!==n-1?r.push([]):r.length)-1].push(l=n),r),[]).map(r=>r.length===1?r[0]:`${r[0]}-${r[r.length-1]}`)
                                                                                                        0
                                                                                                        Попробуй [7,1,1,2,3,3,6,6,7,10,11,17,18,-1].
                                                                                                          0

                                                                                                          Ну, так а в чем проблема то? Дубли что-ли?

                                                                                                            0

                                                                                                            Ну, так а в чем проблема то? Дубли что-ли?
                                                                                                            Фиксится одной строкой в редьюсере, в самом начале:


                                                                                                            if (latest === n) return ranges

                                                                                                            Полный код:


                                                                                                            const getRanges = (numbers) => {
                                                                                                                const normalized = numbers.slice().sort((a, b) => a - b)
                                                                                                                let latest = null
                                                                                                                return normalized.reduce((ranges, n) => {
                                                                                                                    if (latest === n) return ranges
                                                                                                                    const i = latest !== n - 1 ? ranges.push([]) : ranges.length
                                                                                                                    latest = n
                                                                                                                    ranges[i - 1].push(n)
                                                                                                                    return ranges
                                                                                                                }, []).map((range) => {
                                                                                                                    return range.length === 1 ? range[0] : `${range[0]}-${range[range.length - 1]}`
                                                                                                                }).toString()
                                                                                                            }
                                                                                                              0
                                                                                                              Проблемы нету :) Просто указал данные которые не проходят.
                                                                                                              Сейчас все ок. Какая шорт-версия сейчас будет?
                                                                                                                0
                                                                                                                Немного модифицировал
                                                                                                                const getRanges = (numbers) => {
                                                                                                                    const normalized = numbers.slice().sort((a, b) => a - b)
                                                                                                                    let latest = null;
                                                                                                                    return normalized.reduce((ranges, n) => {
                                                                                                                        if (latest === n) return ranges;
                                                                                                                        const i = latest !== n - 1 ? ranges.push([n,n]) : ranges.length;
                                                                                                                        latest = n;
                                                                                                                        ranges[i - 1][1] = n;
                                                                                                                        return ranges;
                                                                                                                    }, [])
                                                                                                                    .map((range) => {
                                                                                                                        return range[0] + (range[0] == range[1] ? ``: `-${range[1]}`);
                                                                                                                    }).toString();
                                                                                                                };
                                                                                                                
                                                                                                            0

                                                                                                            Можно еще на 5 знаков короче:


                                                                                                            const getRanges = (n,l,x='length')=>''+n.slice().sort((a,b)=>a-b).reduce((r,n)=>(r[(l!==n-1?r.push([]):r[x])-1].push(l=n),r),[]).map(r=>r[x]===1?r[0]:r[0]+'-'+r[r[x]-1])
                                                                                                              +2
                                                                                                              Самое грустное в этих собеседованиях, что интервьюеры не пытаются создать подходящую дружескую атмосферу, прежде чем переходить к таким задачкам, над которыми надо поломать голову. Задачку можно брать любую, но чтобы это помогало оценить уровень человека — нужно на интервью именно парным программированием заниматься, а не наблюдать со стороны как кандидат мучается.
                                                                                                                +7
                                                                                                                Я смотрю, не только в Долине в успешные хай-тек компании, как на мёд, слетаются «гении», организовывающие «элитные клубы», после чего в такую компанию нереально попасть людям, просто пишущим рабочий качественный код, который решает поставленную задачу. Только в Гугле и фейсбуке упор делают на мозгодробительные алгоритмы, которые ты должен знать наизусть и выдавать на доске за 30 минут, а тут — вообще зашквар.

                                                                                                                Вот решение в духе Яндекса:

                                                                                                                Интересно, а какая позиция самого Яндекса относительно «духа яндекса» в виде нечитаемого куска кода в стиле «а посмотрите как я могу!», который ни одна вменяемая компания, действительно думающая о
                                                                                                                Такие прогеры эффективны, но, самое главное, взаимозаменяемы:
                                                                                                                в прод в жизни не пропустит?
                                                                                                                  0

                                                                                                                  Задача отбора квалифицированных сотрудников наверное неформализуема и нерешаема в принципе. Даже само понятие «квалифицированный программист» нельзя формализовать.


                                                                                                                  Вот автор статьи считает, что это тот, кто может написать нечитаемый one-liner. По мне читаемость и поддерживаемость кода важнее, так как пишется код один раз, а читается и изменяется затем многократно, причем часто другими людьми. По моему опыту, поддержка кода требует больше времени и усилий, чем написание, поэтому более склонен считать, что важнее писать понятный код.


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


                                                                                                                  Тем не менее, Яндекс, Amazon, Google и Facebook делают большой упор именно на это. ABBYY когда-то здесь писали, что не берут людей без красного диплома. Google вообще одно время нанимал долько PhD, кажется. Возможно, они знают что-то, чего не знаем мы. Все же большие и успешные компании.

                                                                                                                    +1
                                                                                                                    Возможно, они знают что-то, чего не знаем мы.

                                                                                                                    Скорее — они могут себе позволить не самые эффективные и даже странные стратегии найма, так как с доходами у них всё хорошо. Доходами от продуктов, написанных далеко не олимпиадниками-PhD с красными дипломами и черными поясами по написанию кода на бумажке, надо заметить.
                                                                                                                      0

                                                                                                                      Не удивлюсь, если они ведут статистику. Кого взяли на работу: возраст, пол, образование, национальность. Сколько выплачено денег, сколько задач выполнено, какую прибыль принес компании.


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

                                                                                                                        0
                                                                                                                        Не удивлюсь, если они ведут статистику

                                                                                                                        загуглите project oxygen
                                                                                                                      +1
                                                                                                                      Тем не менее, Яндекс, Amazon, Google и Facebook делают большой упор именно на это.

                                                                                                                      Amazon, Google и Facebook в жизни не возьмут человека, написавшего подобный код. Именно Google создал Golang, который так сильно не нравится любителям «смотри как я могу!» из-за того, что заставляет разработчика писать скучный, банальный, но очень легко поддерживаемый код.
                                                                                                                        0
                                                                                                                        Amazon, Google и Facebook в жизни не возьмут человека, написавшего подобный код.

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

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

                                                                                                                      +6
                                                                                                                      И попросил избавиться от ключевых слов async/await. С тех пор это задание стало первым и, в половине случаев, последним на собеседовании — его реально часто заваливают.

                                                                                                                      Сам я ни разу не решал эту задачу до написания этой заметки


                                                                                                                      — у меня у одного вызывает диссонанс сочетание этих двух абзацев?
                                                                                                                        +5

                                                                                                                        Зато честно.

                                                                                                                        0
                                                                                                                        Разрешите влезть со своим решением на жабе :D

                                                                                                                        public static String getRanges(List<Integer> l) {
                                                                                                                          // Begin ranges & End ranges (br & er)
                                                                                                                          List<Integer> br = new ArrayList<>();
                                                                                                                          List<Integer> er = new ArrayList<>();
                                                                                                                        
                                                                                                                          l.stream().reduce(l.get(0)-2, (a, b) -> {
                                                                                                                            if (Math.abs(a-b) > 1 && a < b) {
                                                                                                                              er.add(a);
                                                                                                                              br.add(b);
                                                                                                                            }
                                                                                                                            return b;
                                                                                                                          });
                                                                                                                          er.remove(0);
                                                                                                                          er.add(l.get(l.size()-1));
                                                                                                                        
                                                                                                                          // Zip & join
                                                                                                                          return IntStream
                                                                                                                            .range(0, Math.min(br.size(), er.size()))
                                                                                                                            .mapToObj(i -> (br.get(i) != er.get(i)) 
                                                                                                                                            ? br.get(i) + "-" + er.get(i) 
                                                                                                                                            : br.get(i) + "")
                                                                                                                            .collect(Collectors.joining(", "));
                                                                                                                        }
                                                                                                                        

                                                                                                                        Пс. метод полагает, что вход отсортирован
                                                                                                                          0

                                                                                                                          Долго пытался понять, что такое 1.stream(). Не даром во всех книжках пишут, что испольовать l, I и о в качестве имен переменных — плохая идея.

                                                                                                                          0
                                                                                                                          Код должен быть легко и быстро читаем. Это обеспечивает легкость и небольшие временные затраты на поддержку. Кроме того код может быть передан на поддержку менее клалифицированной команде.
                                                                                                                          Да, что бы быстро читать код, он должен быть минимальной длинны. Но это только один из критериев. Еще более важный критерий, на мой взгляд, — сложность чтения и понимания кода.

                                                                                                                          Вот яркий пример того: ваша имплементация getRanges содержит ошибку, «ручная декомпиляция», дебаг и правка этого места займет много времени. А кроме того, даже зная все это, — вы каждый раз тут споткнетесь взглядом и потратите лишние 5-10 сек, а если вы не часто так пишете — то и до нескольких минут…
                                                                                                                          Не надо из себя строить обфускатор и минимайзер — за вас это легко сделают плагины.
                                                                                                                          В любом случае надо перебрать массив 1 раз, «синтаксический сахар» не сделает код быстрее.

                                                                                                                          Как бы я сделал на шарпе и разбиением 2 на легко читаемых метода
                                                                                                                                  private static string GetRanges(int[] ints) {
                                                                                                                                      //Не ясно условие задачи, надо ли комбинировать всё или только то что находится рядом.
                                                                                                                                      //ints = ints.Distinct().OrderBy(x => x).ToArray(); 
                                                                                                                                      StringBuilder result = new StringBuilder();
                                                                                                                                      for (int index = 0; index < ints.Length; index++) {
                                                                                                                                          int endOfRange = FindEndOfRange(ints, index);
                                                                                                                                          if (endOfRange == index) {
                                                                                                                                              result.Append(ints[index]).Append(',');
                                                                                                                                          } else {
                                                                                                                                              result.Append(ints[index]).Append('-').Append(ints[endOfRange]).Append(',');
                                                                                                                                              index = endOfRange;
                                                                                                                                          }
                                                                                                                                      }
                                                                                                                                      if (result.Length > 0) {
                                                                                                                                          result.Remove(result.Length - 1, 1);
                                                                                                                                      }
                                                                                                                                      return result.ToString();
                                                                                                                                  }
                                                                                                                          
                                                                                                                                  private static int FindEndOfRange(int[] ints, int searchIndex) {
                                                                                                                                      for (int index = searchIndex + 1; index < ints.Length; index++) {
                                                                                                                                          if (ints[index] != ints[index - 1] + 1) {
                                                                                                                                              return index - 1;
                                                                                                                                          }
                                                                                                                                      }
                                                                                                                                      return ints.Length - 1;
                                                                                                                                  }
                                                                                                                          

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

                                                                                                                          Самое читаемое