Как удалить дубликаты из массива чисел и строк без использования Set в JavaScript?
Что проверяют
Понимаете ли вы, как сравниваются значения в JavaScript и во что обходится наивное решение. Запрет на Set нужен, чтобы увидеть, знаете ли вы про приведение ключей объекта к строке. Следом просят оценить сложность и спрашивают, что будет с NaN.
Как отвечать
Покажите два решения и объясните разницу:
filterсindexOf:arr.filter((x, i) => arr.indexOf(x) === i). Коротко и сохраняет порядок, но это O(n²): для каждого элемента идёт поиск по массиву. На тысяче элементов терпимо, на сотнях тысяч уже нет;- объект как словарь: один проход, O(n). Ключи обычного объекта всегда строки, поэтому
1и'1'дадут одинаковый ключ"1". Решение: составной ключtypeof x + ':' + x, тогда число и строка не склеятся; - словарь без прототипа:
Object.create(null)или проверкаObject.hasOwn(seen, key). Это страховка от ключей вроде"toString"и"__proto__", которые иначе совпадут со свойствами прототипа; Map: если запрещён толькоSet,Mapрешает задачу чище всего. Ключи сравниваются по SameValueZero,1и'1'различаются,NaNнаходится.
Проговорите краевые случаи. NaN !== NaN, поэтому indexOf(NaN) возвращает -1, и вариант с filter выбрасывает из результата все NaN. Метод includes использует SameValueZero и NaN находит, так что includes в цикле корректнее, хотя сложность та же. 0 и -0 почти везде считаются одним значением. Если в массиве окажутся объекты, строковый ключ не подойдёт: String({}) для любого объекта даст "[object Object]".
Где спотыкаются
Используют обычный объект с ключом x без учёта типа. Массив [1, '1', 2] превращается в [1, 2], а это и есть главный подвох вопроса.
Называют filter с indexOf решением за O(n), потому что «цикл один». Вызов indexOf внутри колбэка это второй, скрытый проход.
Сортируют массив, чтобы сравнивать соседей. sort() без компаратора сравнивает элементы как строки, [10, 9, 1] станет [1, 10, 9], числа и строки перемешаются, а исходный порядок потеряется.