Задачи с собеседований (front-end) часть 2

    С момента выхода моей предыдущей статьи о вопросах на собеседованиях Задачи с собеседований (front-end) прошло практически два года.

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

    Этот виток собеседований ознаменовался большим обилием алгоритмических задач. Сложно сказать тенденция ли это, или это мне так повезло.

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


    Задача: Написать полифилл для Promise.

    Целесообразность такого вопроса вызывает вопросы, даже если нужно поддерживать пресловутый IE 11 (процент использования которого на момент написания статьи колеблется где-то в районе 0.5% — 1.5% по разным статистическим данным), то есть куча готовых полифиллов.

    Однако, он, вероятно, призван показать знание того, как в принципе работает Promise, что из себя представляет и как с ним работать. Ну что же, можно и с этой стороны зайти, конечно, но можно было бы и просто спросить «как работать с Promise».

    Еще пару раз просто спрашивали теоретически, как бы я реализовала.

    Решение
    function Promise(fn) {
    	this.thenHandlers = [];
    	this.catchHandlers = [];
    	this.isResolved = false;
    	this.isRejected = false;
    
    	setTimeout(() => fn(this.applyResolve.bind(this), this.applyReject.bind(this)));
    }
    
    Promise.prototype = {
    
    	applyResolve: function () {
    		this.thenHandlers.forEach((handler) => handler());
    		this.isResolved = true;
    	},
    
    	applyReject: function () {
    		this.catchHandlers.forEach((handler) => handler());
    		this.isRejected = true;
    	},
    
    	then: function (handler) {
    		if (this.isResolved) {
    			handler();
    		} else {
    			this.thenHandlers.push(handler);
    		}
    
    		return this;
    	},
    
    	catch: function (handler) {
    		if (this.isRejected) {
    			handler();
    		} else {
    			this.catchHandlers.push(handler);
    		}
    
    		return this;
    	}
    
    };
    
    const p = new Promise((resolve, reject) => (
    	Math.round(Math.random() * 10) % 2 === 0
    		? resolve()
    		: reject()
    ));
    
    p
    	.then(function () {
    		console.log('resolved');
    	})
    	.catch(function () {
    		console.log('rejected');
    	});
    


    Задача: Реализовать аналог Promise.all.

    Решение
    function promiseAll(promises) {
    	return new Promise((resolve, reject) => {
    		const results = [];
    		let resolvedCount = 0;
    
    		promises.forEach((promise, index) => {
    			promise
    				.then((result) => {
    					results[index] = result;
    
    					resolvedCount++;
    
    					if (resolvedCount === promises.length) {
    						resolve(results);
    					}
    				})
    				.catch((err) => reject(err));
    		});
    	});
    }
    
    promiseAll([
    	new Promise((resolve) => {
    		setTimeout(() => resolve('foo'), 5000)
    	}),
    
    	new Promise((resolve, reject) => {
    		setTimeout(() => resolve('bar'), 1000);
    	}),
    
    	new Promise((resolve, reject) => {
    		setTimeout(() => {
    			Math.round(Math.random() * 10) % 2 === 0
    				? resolve('baz')
    				: reject(new Error());
    		}, 300);
    	}),
    ])
    	.then((res) => console.log('RESOLVED: ', res))
    	.catch((err) => console.log('REJECTED: ', err));
    


    Задача: Реализовать аналог Function.prototype.bind.

    С появлением Rest parameters реализация этой задачи стала чуть проще, чем прежде, когда приходилось делать arguments.slice.

    Решение
    Function.prototype.bind = function(context, ...argsBind) {
    	const fn = this;
    
    	return function (...args) {
    		return fn.apply(context, argsBind.concat(args))
    	};
    };
    


    Вопрос: Что такое делегирование событий? Плюсы/минусы/подводные камни.

    Ответ
    Делегирование событий — подход при работе с событиями DOM-дерева, при котором обработчики событий добавляются не на каждый конкретный элемент, а только на общий родительский, в то время как необходимость вызова это обработчика для конкретного интересующего нас элемента определяется через ин
    ициатора события, узнать который можно из свойства объекта события event.target.
    Такой подход возможен благодаря особенностям событийной модели DOM-дерева, а конкретно такой особенности, как всплытие событий.

    Плюсы

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

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

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

    Минусы

    Чуть меньшая «прозрачность» и очевидность по сравнению с тем когда обработчик события добавляется непосредственно для нужного элемента.

    Потенциально чуть больше нагрузки ложится на CPU, т.к. любое возникновения события внутри родительского элемента запускает обработчик и поиск необходимого элемента для проверки необходимости работы основного обработчика. Так что тут всё индивидуально, в каждом конкретном случае надо смотреть насколько вероятны возникновения событий, которые будут приходиться «мимо» целевых элементов и что нам более важно оптимизировать — память или CPU.

    Подводные камни

    Любой неосторожный event.stopPropagation может прервать цепочку всплытия события и оно не дойдёт до родительского элемента, на котором установлен обработчик.

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

    Задача: Логическое продолжение предыдущего вопроса — реализовать делегирование.

    Решение
    <div class="wrapper">
    	<div class="child"><div><div><div>click me</div></div></div></div>
    	<div class="child"><div><div><div>click me</div></div></div></div>
    	<div class="child"><div><div><div>click me</div></div></div></div>
    	<div class="other"><div><div><div>dont't click me</div></div></div></div>
    </div>
    

    const delegate = (eventName, el, selector, handler) => {
    	el.addEventListener(eventName, (event) => {
    		let node = event.target;
    		const items = [].slice.call(el.querySelectorAll(selector));
    
    		if (items.length) {
    			while (node !== el && node !== null) {
    				const isTarget = items.some(item => node === item);
    
    				if (isTarget) {
    					handler(node);
    					break;
    				} else {
    					node = node.parentNode;
    				}
    			}
    		}
    	});
    };
    
    delegate('click', document.querySelector('.wrapper'), '.child', (el) => el.style.backgroundColor = 'blue');
    



    Задача: Уникализация значений в массиве.

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

    Например:

    unique([1, 1, 2, 2, 4, 2, 3, 7, 3]); // => [1, 2, 4, 3, 7]
    

    Решение в лоб
    function unique(arr) {
    	const res = [];
    
    	arr.forEach((item) => {
    		if (res.indexOf(item) === -1) {
    			res.push(item);
    		}
    	});
    
    	return res;
    }
    


    Ожидания интервьюера
    function unique(arr) {
    	const res = {};
    
    	arr.forEach((item) => {
    		res[item] = '';
    	});
    
    	return Object.keys(res).map(item => Number(item));
    }
    


    Решение в одну строку
    function unique(arr) {
    	return arr.filter((item, index, self) => (self.indexOf(item) === index));
    }
    


    Задача: «Расплющивание» массива.

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

    Например:

    flat([1, [2, [3, [4,5]]]]); // => [1, 2, 3, 4, 5]
    

    Решение
    function flat(arr) {
    	let res = [];
    	
    	arr.forEach((item) => {
    		if (Array.isArray(item)) {
    			res = res.concat(flat(item));
    		} else {
    			res.push(item);
    		}
    	});
    
    	return res;
    }
    


    Ну или...
    Есть нативный метод — Array.prototype.flat
    Я считаю, что именно с него надо начать ответ на этот вопрос, и только когда (именно когда, а не если) интервьюер скажет, что такое решение ему не подходит и нужно всё сделать руками, приниматься за вышеупомянутое решение через рекурсию.
    [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]].flat(Infinity);
    


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

    Например:

    f([1, 2, null, 7, 8, null, 3]); // => [2, 4, 14, 16, 6]
    

    Решение
    function f(arr) {
    	return arr
    		.filter(item => item !== null)
    		.map(item => item * 2);
    }
    


    Задача: Обход дерева

    Дана структура данных в виде дерева:

    const tree = {
    	value: 1,
    	children: [
    		{
    			value: 2,
    			children: [
    				{ value: 4 },
    				{ value: 5 },
    			]
    		},
    		{
    			value: 3,
    			children: [
    				{ value: 6 },
    				{ value: 7 },
    			]
    		}
    	]
    };
    

    Необходимо написать функцию, возвращающую значения всех вершин дерева:

    getTreeValues(tree); // => [1, 2, 3, 4, 5, 6, 7]
    

    Решение
    Через рекурсию:

    function getTreeValues(tree) {
    	let values = [ tree.value ];
    
    	if (Array.isArray(tree.children)) {
    		tree.children.forEach(item => values = values.concat(getTreeValues(item)));
    	}
    
    	return values;
    }
    

    Через цикл:

    function getTreeValues(tree) {
    	const tmpTree = [tree];
    	const res = [];
    	let current;
    
    	while (tmpTree.length > 0) {
    		current = tmpTree.shift();
    		res.push(current.value);
    
    		if (current.children) {
    			current.children.forEach(item => tmpTree.push(item));
    		}
    	}
    
    	return res
    }
    


    Задача: Сумма вершин дерева

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

    Решение
    Через рекурсию:

    function getTreeSum(node) {
    	let sum = node.value;
    
    	if (Array.isArray(node.children)) {
    		node.children.forEach(item => sum += getTreeSum(item));
    	}
    
    	return sum;
    }
    

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

    Задача: Сортировка нечётных.

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

    Например:

    oddSort([7, 3, 4, 9, 5, 2, 17]); // => [3, 5, 4, 7, 9, 2, 17]
    

    Решение
    function oddSort(arr) {
    	arr.forEach((item, index) => {
    		if (item % 2 === 1) {
    			let sortNumber = item;
    
    			for (let i = 0; i < index; i++) {
    				if (arr[i] % 2 === 1) {
    					if (arr[i] > sortNumber) {
    						const tmp = sortNumber;
    
    						sortNumber = arr[i];
    						arr[i] = tmp;
    					}
    				}
    			}
    			arr[index] = sortNumber;
    		}
    	});
    
    	return arr;
    }
    


    Задача: Идентичный алфавит

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

    Например:

    isEqualSymbols('кот', 'ток'); // => true
    isEqualSymbols('кот', 'тик'); // => false
    

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

    Решение
    function isEqualSymbols(str1, str2) {
    	if (str1.length !== str2.length) {
    		return false;
    	}
    
    	if (str1.split('').sort().join('') === str2.split('').sort().join('')) {
    		return true;
    	}
    
    	return false;
    }
    

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

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

    Задача: Бомба

    Надо реализовать «бомбу» (в виде функции-конструктора), которая получает на входе время, через которое взорвется и некоторый «звук взрыва» (строку, которую вернет через заданное время). С фантазией задача.

    Решение
    function Bomb(message, delay) {
    	this.message = message;
    
    	setTimeout(this.blowUp.bind(this), delay * 1000); // взрываем через delay sec
    }
    
    Bomb.prototype.blowUp = function () {
    	console.log(this.message);
    };
    
    new Bomb("Explosion!", .5);
    


    Задача: «Сжатие строк»

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

    Например:

    rle('AVVVBBBVVXDHJFFFFDDDDDDHAAAAJJJDDSLSSSDDDD'); // => 'AV3B3V2XDHJF4D6HA4J3D2SLS3D4'
    

    Решение
    function rle(str) {
    	const result = [str[0]];
    	let count = 1;
    
    	for (let i = 1; i < str.length; i++) {
    		if (str[i] === str[i - 1]) {
    			count++;
    
    			if (i === str.length - 1) {
    				result.push(str[i]);
    				if (count > 1) {
    					result.push(count);
    				}
    			}
    		} else {
    			if (i > 1) {
    				result.push(str[i - 1]);
    			}
    
    			if (i === str.length - 1) {
    				result.push(str[i]);
    			}
    
    			if (count > 1) {
    				result.push(count);
    			}
    
    			count = 1;
    		}
    	}
    
    	return result.join('');
    }
    


    Вопрос: Что получится в результате выполнения кода и почему?

    var obj = {};
    
    function func(x) {
    	x = 1;
    
    	return x;
    }
    
    func(obj); // => ?
    console.log(obj); // => ?
    

    Ответ
    Функция вернёт 1, obj при этом не изменится;
    Несмотря на то, что объекты в JavaScript передаются в параметры функций по ссылке, obj не изменится.

    Внутри функции создаётся локальная переменная x, в которую изначально попадет ссылка на obj, но позже эта переменная переписывается на числовое значение 1. Т.е. меняется само значение переменной x, но меняется значение, которое находится по ссылке, переданной изначально в функцию.

    Вопрос: Как передать изображение размером 10Mb с помощью GET-запроса?

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

    P.S.: Вопрос из разряда тех, на которые нет правильного ответа, потому как единственно верным ответом на этот вопрос был бы — не делайте так, не отправляйте файлы методом GET, даже не думайте об этом и всё будет хорошо.

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

    Вопрос: Назовите известные вам HTTP-методы. Что такое CRUD?

    Ответ
    Про HTTP-методы и CRUD:
    GET — read — используется только для получения данных.
    POST — create — создание новых сущностей.
    PUT/PATCH — update — обновление данных.
    DELETE — delete — удаление.

    Вообще HTTP-методов сильно больше, помимо выше перечисленных есть OPTIONS, HEAD, TRACE и др.

    Есть ещё пул вопросов, связанных с фреймворками и смежными с frontend'ом темами, возможно оформлю их в отдельную статью.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 34

      0
      Задача: Уникализация значений в массиве.

      developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Set
        +1
        const funique = (arr) => [...new Set(arr)];
        
          0
          На собеседовании была такая задача, про Set сразу не подумал, решил проходом по массиву — сказали что правильно, но через Set лучше и элегантнее.

          Через неделю другое собеседование, задача та же, делаю через Set — сказали что правильно, но они хотят посмотреть на знание перебирающих методов массива и попросили сделать по другому)
        0

        Со сплющиванием массива есть тоже забавный хак:


        const flat = (arr) => arr.join().split(',')
        
        flat([1, [2, [3, [4,5]]]]); // => [1, 2, 3, 4, 5]
          +3

          только в итоге не числа, а строки в массиве

            0
            В условии задачи не было сказано, что этот массив состоит из чисел. Если toString() элемента массива вернёт запятую — забавный хак превратится в забавный баг.
            0
            Какой процент из подобных задач встречался вам на работе?
            Вот зачем так делают, можно же гораздо проще: дать задачу прямо из проекта. Спросить как кандидат реализовывал бы её. Увидеть ход его мышления, порядок его действий при выполнении реальной задачи. Например: «У нас есть задача сделать функционал фильтрации продуктов по такому-то параметру. С чего бы ты начал и как бы попытался её сделать?», и кандидат сразу начинает думать: «Так, ну я бы сначала уточнил это и это, потом сделал бы это и потом проверил как всё это работает и как это можно еще улучшить...». Если прям вот нужно посмотреть как он будет писать код, можно попросить его написать что-нибудь прямо из контекста этой задачи, например ту же сортировку, только не козлов и баранов, а реального кейса из проекта.

            А эта вся ерунда на собеседованиях гуглится за 2 минуты если будет надо, смысл вот давать эти задачи?
              0
              Еще на алгоритмы некоторые ребята дают… Тоже за то, чтобы узнавать у собеседующихся об их опыте путем проектирования решения для конкретных рабочих задач. Механика воплощения этих проектов будет зависеть от фреймворка и архитектуры, от подходов, использующихся в проекте, и, как правило, с вдохновением из StackOverflow, а не кодингом «с нуля». Но от юниоров, закончивших какие-нибудь курсы, хотелось бы ожидать как раз умения работать с чистым языком и логикой, так как опыта в решении реальных задач у них, как правило, нет, так что подобные задачки подойдут.
                0
                Какой процент из подобных задач встречался вам на работе?

                Если только сортировка по уникальности. Ну как бы, когда приходишь на собес, тебя не спрашивают: «Хочешь такую задачу порешать, а может тебе не понравилась эта, можем на выбор предложить эту и эту».Увы, так не бывает. Ты или делаешь ту, что дают, или «мы вам перезвоним».
                Если бы я проводила собеседование, стала бы я давать такие задачи, точно нет.

                А эта вся ерунда на собеседованиях гуглится за 2 минуты если будет надо, смысл вот давать эти задачи?

                Может они прекрасно гугляться, но на собесе же не достанешь мобильный и не начнешь искать решение задачи в интернете? В противном случае результат: «мы вам перезвоним».
                  0
                  Я бы не бухтел при выдаче подобных задач, только если бы очень хотел попасть именно в эту контору и условия были бы отличными. Но если меня собеседует совсем «зеленый» человек и говорит что надо использовать двойное равенство вместо тройного, потому что так короче, и дает задачу написать сортировку «пузырьком», то чешу репу и говорю «я вам перезвоню»)) Не шучу, бывало и такое, причем на собеседовании на сеньорские позиции.
                0
                Задача: Написать функцию, принимающую аргументом массив чисел и возвращающую новый массив, состоящий из удвоенных значений первого.


                Убираем два прогона массива и получаем тот же результат

                function f(arr = []) {
                  const result = [];
                
                  arr.reduce((acc, item) => {
                    if (typeof item === 'number') {
                      acc.push(item * 2);
                    }
                
                    return acc;
                  }, result);
                
                  return result;
                }
                
                f([1, 2, null, 7, 8, null, 3]); // => [2, 4, 14, 16, 6]
                


                Так же добавлю, что в массиве могут быть string/undefined/boolean типы, в общем любой тип, отличный от null. Поэтому лучше не проверять условие как привел автор (i !== null).
                  0
                  Ну так, все равно один прогон по массиву у вас остался, но в то же время увеличилось время для восприятия кода.
                    0
                    Внимательно посмотрите на пример автора и посчитайте сколько прогонов по массиву у него.
                      0
                      В статье два прогона, один фильтром, один map`м. В комменте используется reduce, который тоже проходит по всему массиву, переданному в качестве аргумента функции, и последовательно перебирает элементы этого массива. В итоге выигрыш в один прогон.

                      Метод «arr.reduce(callback[, initialValue])» используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата.

                      Это один из самых сложных методов для работы с массивами.

                      learn.javascript.ru — Массив: перебирающие методы
                    0

                    В таком случае нужно дополнительно сделать проверку на NaN
                    typeof NaN === 'number' // true

                    +2
                    Реализовывать полифилл для Promise, используя const и стрелочные функции…
                    Где он заработает, интересно?
                      0

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

                        0
                        На собеседование в МТС попросили реализовать promise(я с ходу не смогла написать),
                        в Wildberries — Promise.all и еще три задачи из перечисленных(здесь решила все четыре),
                        задача из Mail:
                        var obj = {};
                        function func(x) {
                        	x = 1;
                        ...
                        

                        в СТС — «сжатие строк»,
                        Комсомольская правда — обход дерева.

                        Как бы большинство не шарашкины конторы с предлагаемой зп выше 150К и всякими ништяками, бонусами. Выбирала по зп, относительно близком ко мне территориальном расположении и что бы компания была более, менее известной.
                        Это я еще в Яндекс не ходила, вот они славятся своими задачами на алгоритмы.
                        0
                        А кто сейчас в здравом уме будет использовать самописный промис? Да и необходимость использовать сейчас проверенный полифил не особо-то актуальна, это только если требуется поддержка ie11 или Opera Mini. Остальные приличные браузеры уже давно поддерживают. Да и к тому же, если можешь написать промис, то какие проблемы подправить код с const на var, а стрелочные функции на обычные, где надо прибиндив контекст.

                        Но на мой взгляд, не очень подходящий вопрос для собеса.
                          0
                          Но в любом случае задача «написать полифилл» и задача «изложить алгоритм Promise, не используя Promise» — это разные задачи.
                            0

                            никто не пишет свои промисы костылей ради. но вы удивитесь, что во многих часто используемых библиотеках (решил проверить около полугода или года назад) промисы реализовывают через setTimeout(), что в корне не верно.


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


                            https://html.spec.whatwg.org/multipage/webappapis.html#event-loops


                            The microtask queue is not a task queue.
                          0

                          По поводу третьей с конца задачи, зачем так писать код? Чтобы было что спрашивать на собеседовании? :)

                            0
                            По поводу третьей с конца задачи, зачем так писать код?

                            За такое на первом же ревью а-та-та.

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

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

                              0
                              На какой уровень разработчика дают такие задачи? Junior? Middle?
                                0
                                Я расцениваю себя как middle, но вот провести четкую границу между джун, мидл, сеньор очень сложно.
                                  +1
                                  Спасибо большое за ответ!
                                0

                                Тут уже обращали внимание на реализацию Промиса, но почему-то никто не обратил внимания, что она неверна:


                                1. Промис не возвращает значение (resolve() и reject() не принимают аргументы)
                                2. then() и catch() вместо нового промиса возвращают тот же самый.
                                3. Следствие пункта 2 — не важно, в каком порядке в цепочке расположены then() и catch() — выполнятся только один вид коллбеков.
                                4. Ещё одно следствие 2 — возврат из любого коллбека промиса также руботать не будет.
                                5. Все then()/catch() коллбеки вызываются одновременно — не очень понятно, зачем вообще тогда больше одного then()
                                6. Ну и throw в любом из коллбеков просто выкинет ошибку и прервет выполнение всей цепочки.
                                  В результате это даже близко не похоже на промис — я бы назвал это одноразовым EventEmitter'ом.
                                  0
                                  Ещё не хватает вызова колбеков then()/catch() в микротасках, концепции thenable, и прочая и прочая…
                                  Моё имхо — промисный полифил слишком громоздкий, чтобы быть задачей для собеседования.
                                  0
                                  Настолько приблизительного «полифила» для промиса я ещё не видел :)

                                  Рекурсивный обход дерева не выдает требуемый порядок, потому что «поиск в глубину». Нерекурсивный — норм., там «поиск в ширину».

                                  Ну и RLE можно в одну строку
                                  const rle = s => s.replace(/(.)\1+/g, (m, c) => c + m.length);
                                  
                                    0
                                    Задача: Сортировка нечётных.

                                    Здравствуйте. А почему так сложно? Можно же попроще:
                                    function oddSort(arr) {
                                    //отобрать только нечетные и отсортировать
                                        odd_sorted = arr.filter((x)=> x%2==1 ).sort((a,b)=>a-b);
                                    //вставить их на нечетные места
                                        return arr.map(item => {
                                            return (item%2 != 0)? odd_sorted.shift() : item;
                                        });   
                                    }
                                      0
                                      У вас получилось так же тяжелое решение. Метод .shift() занимает O(N), т.е. удаляет первый элемент и все остальные сдвигает на -1 позицию. И того, если у вас M нечетных чисел, в итерации arr.map(...) (K — всего чисел), сложность будет O(M*K), что, при тесте, где все числа нечетные, выйдет O(K^2) (так как M === K). В некоторые компании, такие решения не принимаются(
                                      Чтоб это исправить, достаточно создать указатель элементы в odd_sorted:
                                      function oddSort(arr) {
                                      //отобрать только нечетные и отсортировать
                                          odd_sorted = arr.filter((x)=> x%2==1 ).sort((a,b)=>a-b);
                                      //вставить их на нечетные места
                                         let i = 0; // увеличиваем его каждый раз, после вставки нечетных в массив
                                          return arr.map(item => {
                                              return (item%2 != 0)? odd_sorted[i++] : item;
                                          });   
                                      }
                                      

                                      что даст сложность O(2K + MlogM) (уже не квадрат)
                                        0
                                        Да, спасибо! Ваш вариант эффективнее.

                                    Only users with full accounts can post comments. Log in, please.