<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Set vs Array: удаляем дубликаты в тегах без лишних костылей]]></title><description><![CDATA[<p dir="auto">Когда нужно обработать теги поста и оставить только уникальные значения, половина разработчиков тянется к привычным методам массивов, а потом удивляется, почему код работает медленнее, чем хотелось бы. На самом деле задача решается в одну строку, если знать нужный инструмент.</p>
<p dir="auto">В этой статье разберёмся, почему <strong>Set</strong> - это не просто красивое решение, а настоящий выигрыш по производительности. Посмотрим, как он работает под капотом, какие ошибки подстерегают новичков, и когда Array всё же может быть полезнее.</p>
<h2>Чем Set отличается от Array</h2>
<p dir="auto">Массив - это <strong>индексированная коллекция</strong>. Значения упорядочиваются по позиции: нулевой элемент, первый, второй. Каждый раз, когда нужно проверить, есть ли уже такой элемент, приходится либо использовать <code>indexOf()</code>, либо перебирать весь массив циклом. Для массива с тысячей тегов это быстро становится боль.</p>
<p dir="auto">Set - это совсем другое животное. Это <strong>коллекция ключей</strong>, где каждое значение хранится только один раз. Когда вы пытаетесь добавить дубликат, Set просто его проигнорирует. Нет никаких проверок перед вставкой - это встроено в саму структуру данных.</p>
<ul>
<li><strong>Индексированность</strong>: Array упорядочивает по индексам (0, 1, 2…), Set упорядочивает по ключам</li>
<li><strong>Уникальность</strong>: Array хранит всё, Set автоматически отбрасывает дубликаты</li>
<li><strong>Производительность проверки</strong>: Array требует линейного поиска, Set проверяет за O(1)</li>
<li><strong>Порядок элементов</strong>: Оба сохраняют порядок вставки, но только Array позволяет быстро обратиться по индексу</li>
</ul>
<h2>Как Set обрабатывает примитивы и объекты</h2>
<p dir="auto">Вот тут начинается самое интересное. Set работает с примитивами (строки, числа, boolean) по <strong>значению</strong>, а с объектами по <strong>ссылке</strong>.</p>
<p dir="auto">Если у вас массив тегов вроде <code>['react', 'vue', 'react', 'angular']</code> - Set справится за миллисекунду. Преобразовываем в Set и обратно в массив:</p>
<pre><code class="language-javascript">const tags = ['react', 'vue', 'react', 'angular'];
const uniqueTags = [...new Set(tags)];
// Результат: ['react', 'vue', 'angular']
</code></pre>
<p dir="auto">Но если вы решили хранить теги как объекты <code>{name: 'react'}</code>, вот тут Set ловит косяк. Даже если два объекта выглядят идентично, это разные ссылки в памяти:</p>
<pre><code class="language-javascript">const tagObjects = [{id: 1}, {id: 1}, {id: 1}];
const uniqueTagObjects = new Set(tagObjects);
console.log(uniqueTagObjects.size); // 3, не 1!
</code></pre>
<p dir="auto">Почему так? Потому что Set сравнивает объекты по ссылке, а не по содержимому. Каждый <code>{id: 1}</code> - это разные адреса в памяти. Если вам действительно нужно дедублицировать объекты, придётся писать свою логику или использовать Map с кастомным ключом.</p>
<ul>
<li><strong>Примитивы</strong>: Сравниваются по значению - два одинаковых числа считаются дубликатом</li>
<li><strong>Объекты</strong>: Сравниваются по ссылке - два объекта с одинаковыми свойствами это разные элементы</li>
<li><strong>Смешанные типы</strong>: <code>1</code> (число) и <code>'1'</code> (строка) - разные значения, Set оба сохранит</li>
<li><strong>Null и undefined</strong>: Сохраняются по одному экземпляру</li>
</ul>
<h2>Производительность: цифры говорят сами за себя</h2>
<p dir="auto">Если вы слышали, что Set быстрее - это не маркетинг, это факт. Тесты на реальных объёмах данных показывают: Set быстрее Array с filter в <strong>десятки раз</strong>, а reduce в <strong>сотню раз</strong>.</p>
<p dir="auto">Когда вы вызываете <code>arr.indexOf()</code> или <code>arr.includes()</code> для проверки наличия элемента, браузер проходит весь массив с начала. Это O(n) операция. Повторяем проверку для каждого элемента - получаем O(n²). Set использует хеш-таблицу внутри и проверяет за O(1). Для массива из тысячи элементов разница измеряется в порядках величины.</p>
<p dir="auto">Сравните эти подходы:</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Подход</th>
<th>Код</th>
<th>Скорость</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Set</strong></td>
<td><code>[...new Set(arr)]</code></td>
<td>Базовая</td>
</tr>
<tr>
<td><strong>Filter + indexOf</strong></td>
<td><code>arr.filter((v, i) =&gt; arr.indexOf(v) === i)</code></td>
<td>~10x медленнее</td>
</tr>
<tr>
<td><strong>Reduce</strong></td>
<td><code>arr.reduce((acc, v) =&gt; acc.includes(v) ? acc : [...acc, v], [])</code></td>
<td>~100x медленнее</td>
</tr>
<tr>
<td><strong>Цикл с проверкой</strong></td>
<td><code>for</code> + <code>arr.includes()</code></td>
<td>~20x медленнее</td>
</tr>
</tbody>
</table>
<pre><code class="language-javascript">// Измеряем самый быстрый способ
const testArray = Array.from({length: 10000}, (_, i) =&gt; i % 100);

console.time('Set approach');
const result1 = [...new Set(testArray)];
console.timeEnd('Set approach'); // ~1ms

console.time('Filter approach');
const result2 = testArray.filter((v, i) =&gt; testArray.indexOf(v) === i);
console.timeEnd('Filter approach'); // ~50ms
</code></pre>
<h2>Методы Set для работы с тегами</h2>
<p dir="auto">Kогда вы знаете, какие методы есть, работать с Set становится проще. Вот что вам нужно для обработки тегов:</p>
<ul>
<li><code>set.add(value)</code> - добавить тег в множество, дубликаты игнорируются автоматически</li>
<li><code>set.has(value)</code> - проверить, есть ли тег уже в множестве (быстро, за O(1))</li>
<li><code>set.delete(value)</code> - удалить конкретный тег</li>
<li><code>set.clear()</code> - очистить всё множество</li>
<li><code>set.size</code> - узнать количество уникальных тегов</li>
</ul>
<p dir="auto">Практический пример - обработка тегов поста:</p>
<pre><code class="language-javascript">const postTags = new Set();

// Добавляем теги пользователя
postTags.add('javascript');
postTags.add('frontend');
postTags.add('javascript'); // Игнорируется

// Проверяем наличие
if (postTags.has('javascript')) {
  console.log('Тег уже есть');
}

// Итерируем в порядке добавления
postTags.forEach(tag =&gt; console.log(tag));
// javascript, frontend

// Удаляем ненужное
postTags.delete('frontend');
console.log(postTags.size); // 1
</code></pre>
<p dir="auto">Итерация всегда происходит в порядке вставки элементов. Это важно - если вы хотите сохранить порядок тегов, как их вводил пользователь, Set не подведёт.</p>
<h2>Когда Array ещё может быть полезен</h2>
<p dir="auto">Не преувеличивайте, Set - не серебряная пуля. Есть сценарии, где Array всё ещё нужен.</p>
<p dir="auto">Если вам нужно <strong>частно обращаться по индексу</strong> - Array выигрывает. <code>tags</code> это O(1), а из Set элемент по номеру не достать. Также если вы работаете с <strong>очень маленькими коллекциями</strong> (пара десятков элементов), производительность Set не будет видна, а код может быть понятнее с привычным Array.</p>
<p dir="auto">Для древних браузеров (IE10 и ниже) Set просто не существует - придётся fallback’ить на Array. И если вы делаете какой-то специфический <strong>обход или трансформацию</strong> данных, Array методы могут быть удобнее.</p>
<pre><code class="language-javascript">// Array vs Set: когда Array проще
const tags = ['react', 'vue', 'angular'];

// Нужна трансформация каждого элемента
const tagsByLength = tags.map(tag =&gt; tag.length);
// Set это не может делать встроенно

// Нужна фильтрация по условию
const longTags = tags.filter(tag =&gt; tag.length &gt; 5);
// Set тоже не может

// Нужен доступ по индексу
const firstTag = tags;
// Set заставит конвертировать в массив
</code></pre>
<h2>Практический паттерн: обработка тегов поста</h2>
<p dir="auto">Большинство задач с дедупликацией тегов решаются одинаково. Вот универсальный подход, который работает в 90% случаев:</p>
<pre><code class="language-javascript">function getUniqueTags(rawTags) {
  // rawTags может быть грязным - с пробелами, дубликатами, пустыми строками
  return [
    ...new Set(
      rawTags
        .map(tag =&gt; tag.trim().toLowerCase())
        .filter(tag =&gt; tag.length &gt; 0)
    )
  ];
}

const userInput = ['React', 'REACT', ' vue ', '', 'Angular', 'react'];
const cleanTags = getUniqueTags(userInput);
console.log(cleanTags);
// ['react', 'vue', 'angular']
</code></pre>
<p dir="auto">Всё чётко: сначала нормализуем данные через map и filter, потом Set отрезает дубликаты, потом разворачиваем обратно в массив.</p>
<ul>
<li><strong>Нормализация перед Set</strong>: Приводим к одному формату (lowercase, trim), убираем пустоту</li>
<li><strong>Set деплицирует</strong>: Одна операция, всё работает</li>
<li><strong>Разворот в массив</strong>: Если нужен массив дальше, используем spread или Array.from()</li>
<li><strong>Сохранение порядка</strong>: Set гарантирует порядок вставки</li>
</ul>
<h2>На чём не стоит зацикливаться</h2>
<p dir="auto">Есть несколько заблуждений, которые прилипают к Set. Первое - что Set это чудо-решение для всего подряд. Нет, это инструмент для конкретной задачи. Второе - что объекты в Set как-то магически дедублицируются. Нет, они сравниваются по ссылке, и это нормально.</p>
<p dir="auto">Третье заблуждение - что нужны сложные полифиллы для старых браузеров. Set поддерживается везде, где нужно (IE11+, современные браузеры). Если вы ещё поддерживаете IE10, это вообще отдельная проблема, не только для Set.</p>
<p dir="auto">И последнее - что производительность Set магична в каждом случае. Set быстрее именно на проверках уникальности и дедупликации больших объёмов. На маленьких массивах разница незаметна.</p>
]]></description><link>https://forum.exlends.com/topic/2041/set-vs-array-udalyaem-dublikaty-v-tegah-bez-lishnih-kostylej</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 21:33:05 GMT</lastBuildDate><atom:link href="https://forum.exlends.com/topic/2041.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 09 Apr 2026 10:36:58 GMT</pubDate><ttl>60</ttl></channel></rss>