- Наборы
- Создание набора
- Добавление значений в набор
- Удаление значений из набора
- Проверка подсчета элементов в наборе
- Проверка, если элемент находится в наборе
- Сбросить набор
- Пройти через установленные значения
- Преобразование набора в массив
- Удаление объектов и массивов дубликатов
- Json.Stringify Проблема
- Работа с объектами с одинаковыми значениями, но с разным ключевым порядком
- Работа с экземплярами занятий
- Работа с NAN, нулевым и неопределенным
- Дело с функциями
- Gotchas
- VUE Реактивность
- Прокси -набор
- Примитивные и эталонные типы
Установить является конструктором для коллекции уникальных элементов JavaScript. Его можно использовать, чтобы легко управлять списками идентификаторов и других примитивных типов.
Его можно использовать для написания более семантического кода, удаления дубликатов или записи состояний на основе идентификаторов объектов, например.
Создание набора
Вы можете создать набор, используя Новый ключевое слово и вызов его напрямую или с значением для использования в качестве базы.
const shoppingList = new Set(); // javascript => [] const shoppingList = new Set(); // typescript => string[] const shoppingList = new Set (['a', 'a', 'b']); // ['a', 'b'] as string[] const uniqueLetters = new Set (['aab']); // ['a', 'b'] as string[]
Добавление значений в набор
Чтобы добавить значение к набору, вам просто нужно вызвать .add метод Он не добавит элемент, если он уже находится в наборе.
const shoppingList = new Set(['pizza']);
shoppingList.add('meat');
// you can also chain it, but unfortunately you can only pass one value each time.
shoppingList
.add('meat')
.add('coke')
Если бы вы использовали массивы, вам нужно было бы делать что -то подобное каждый раз
// Using arrays this would be equivalent to
const shoppingList = ['pizza'];
if (!shoppingList.includes('meat')) {
shoppingList.push('meat');
}
Итак, с Набор Вы можете сделать этот процесс немного проще.
Удаление значений из набора
Чтобы удалить значение из набора, вам просто нужно вызвать .delete метод Преимущество подхода массива состоит в том, что его можно легко применить к любому значению в наборе, и размер набора изменяется после удаления, а с массивами вы получите пустые слоты.
const shoppingList = new Set(['pizza']);
shoppingList.delete('meat'); // returns false since 'meat' was not in the list. Set stays the same.
shoppingList.delete('pizza'); // Returns true since the element was in the set. The set size is now 0.
Это проще и более семантическое, чем дело с массивами для случаев, когда ценность находится в середине.
// Given a base array
const shoppingList = ['pizza', 'coke', 'chocolate'];
// If you wanted to remove the last element it would be simple
shoppingList.pop();
// The first element too
shoppingList.shift();
// But for an element somewhere in the middle it gets a little more complicated.
// You could do this.
delete shoppingList[1]; // But it would create an empty space in the array :(
// So instead you need to do something like this
if (shoppingList.includes('meat')) {
// Which can be bad as it resets the object reference.
shoppingList = shoppingList.filter(item => item !== 'meat');
}
Проверка подсчета элементов в наборе
Отличается от массивов, где вы получаете доступ к длина свойство, с наборами, вам нужно получить доступ к размер собственность вместо.
const shoppingList = new Set(['pizza']); shoppingList.size // 1
Проверка, если элемент находится в наборе.
Чтобы увидеть, находится ли элемент в наборе, который вы используете .has метод
const shoppingList = new Set(['pizza']);
shoppingList.has('pizza') // true
С массивами это также довольно просто
const myArray = ['one', 'two'];
myArray.includes('two') // true
Сбросить набор
Вы можете сбросить набор, позвонив в .clear Метод:)
const shoppingList = new Set(['pizza']);
shoppingList.size // 1
shoppingList.clear();
shoppingList.size // 0
shoppingList.has('pizza') // false
С помощью массивов вы можете просто установить его на новый, но если вы хотите сохранить справочник нетронутой, вам нужно будет использовать .pop Несколько раз, поэтому с наборами это проще.
const x = { a: [1,2,3] }
const myArray = x.a;
x.a = [];
console.log(x.a); // []
console.log(myArray) // [1,2,3] :(
x.a = myArray;
myArray.pop();
myArray.pop();
myArray.pop();
console.log(x.a); // [] :)
console.log(myArray) // [] :)
Пройти через установленные значения
Для наборов вы можете использовать либо .foreach Метод или Для стоимости myset Анкет
const mySet = new Set([1,1,2,3,4,5]);
mySet.forEach(cb);
for (const item of mySet) { // only "of" works. The "in" will not work.
//...
}
Преобразование набора в массив
Преобразование массива в набор, затем преобразование установки обратно в массив – это простой трюк, который вы можете сделать, чтобы удалить из него дублированные значения:)
Чтобы преобразовать из массива в установку, вам просто нужно передать его в аргументе конструктора SET.
Чтобы преобразовать из набора в массив, вы можете использовать Array.from () или деконструирование внутри нового массива.
const thingsIWant = ['cake', 'pizza', 'pizza', 'chocolate']; const shoppingList = Array.from(new Set(thingsIWant)); // will output: ['cake', 'pizza', 'chocolate'] const shoppingList = [...new Set(thingsIWant)]; // Same as above, but shorter
Удаление объектов и массивов дубликатов
Объекты и массивы являются эталонным типом, что означает набор ( ) удалит только дублированные ссылки, но не структуры.
бывший:
const x = { a: 1 };
[...new Set([x, x])] // Will result in [x]
[...new Set([x, { a: 1 }])] // Will result in [x, {a: 1}]
// same for arrays...
Простой обходной путь над этим – json.parse и .map
бывший:
const x = { a: 1 };
[...new Set([x, { a: 1 }].map(JSON.stringify))].map(JSON.parse); // [{ a: 1 }]
Есть некоторые недостатки:
- Он не будет работать, если у вас есть одинаковые структуры, но с различным заказом свойства (например:
{a: 1, b: 2}и{b: 2, a: 1}) - Json.stringify будет конвертировать функции в неопределенные
- Json.stringify преобразует Nan в “null”
- Json.stringify return
неопределенноедлянеопределенное, но json.parse не может справиться с этим. - Json.stringify не будет работать должным образом с классами и другими
Json.Stringify Проблема
Бывший:
const x = [undefined, null, NaN, true, 'asd', {a: 5}, () => {
console.log('a')
}, new Set(['asd', 'bbb'])].map(JSON.stringify);
console.log(x) // [ undefined, "null", "null", "true", "\"asd\"", "{\"a\":5}", undefined ]
x.map(JSON.parse) // will throw an error parsing the first value
Одним из возможных решений здесь было бы удалить эти неопределенные значения и добавить его обратно позже после повторного анализа:
const x = [undefined, 'asd', true, false, { a: 1 }, { a: 1 }];
// map to json so we don't remove valid falsy values
const jsonX = x.map(JSON.stringify); // [ undefined, "\"asd\"", "true", "false", "{\"a\":1}", "{\"a\":1}" ]
// Create the set to remove duplicates
const uniqueJsonX = [...new Set(jsonX)] // [ undefined, "\"asd\"", "true", "false", "{\"a\":1}" ]
// Now we remove the values that cannot be parsed. Since we conveted false to "false" before, this will only remove non-parseable values.
const parseableJsonX = uniqueJsonX.filter(v => v); // [ "\"asd\"", "true", "false", "{\"a\":1}" ]
// Now we can parse the array with JSON.parse to get our "original" values back :)
const parsed = parseableJsonX.map(JSON.parse); // [ "asd", true, false, {…} ]
// And finally, if you want to also add undefined values to the set again.
const parsedWithInvalid = x.filter(v => !v)];
// Or if you want to add functions and others that were removed too
const parsedWithInvalid = x.filter(v => !JSON.stringify(v)];
const uniqueX = [...new Set([...parsed, ...x.filter(v => !v)])]; // [ "asd", true, false, {…}, undefined ]
Ну, это решает большинство упомянутых проблем. Но как насчет объектов с различным порядком, функциями и экземплярами классов?
Работа с объектами с одинаковыми значениями, но с разным ключевым порядком
Чтобы решить эту проблему, мы должны добавить новый шаг в решение выше. В этом случае, чтобы быстро сортировать значения объекта, мы можем сопоставить его с объектом.
const myObject = {c: '3', b: '2', a: '1'};
const myObject2 = {a: '1', b: '2', c: '3'};
const myArr = [myObject, myObject2].map(item => {
return Object.fromEntries(Object.entries(item).sort());
}).map(JSON.stringify);
console.log([...new Set(myArr)].map(JSON.parse)); // [{ a: '1', b: '2', c: '3'}]
Работа с экземплярами занятий
Классы могут вести себя неожиданно, когда вы ходите по борьбе Json.stringify () , нравится:
const x = new Date();
console.log(JSON.stringify(x)); // will output date string instead of [object Date]
const y = new Set([1,2,3,4]);
console.log(JSON.stringify(y)); // {} 🤔
Однако это может работать, если у вас есть простой объектный класс, но в целом небезопасно включать в себя в набор для удаления дубликатов.
Я бы рекомендовал разделить в начале подхода, упомянутого ранее, создавая новый набор для него (Если вы хотите удалить дублированные экземпляры) и присоединиться к ним в результате в конце.
const base = [undefined, 'asd', true, false, { a: 1 }, { a: 1 }, new Set([1,2,3], new Date())];
const state = {
notParseable: []
parseable: []
};
for (const key in base) {
const isObject = typeof base[key] === 'object';
const isSimpleObject = isObject && base[key].toString() !== '[object Object]';
if (!base[key] || isSimpleObject) {
state.notParseable.push(base[key]);
continue;
}
state.parseable.push(base[key]);
}
// ...
return [...result, ...[...new Set(state.notParseable)]];
Работа с NAN, нулевым и неопределенным
Чтобы удалить дубликаты этих, тот же подход, что и решение выше, можно использовать:).
В этом случае мы удаляем его из значений, которые пройдут через json.stringify и создаем отдельный набор для него, а затем присоединяемся к нему в конце.
Дело с функциями
С помощью функций вы также можете отфильтровать его заранее и удалить дублированные ссылки.
a = () => {};
new Set([a, a]) // Set [ a() ]
Однако, если вы хотите сравнить между подъемами, по какой -то причине, вероятно, было бы лучше сделать это в массиве, как это.
const x = [() => {}, () => {}];
const uniqueFunctions = [];
const stringifiedFunctions = [];
for (const f of x ) {
if (!stringifiedFunctions.includes(f.toString())) {
uniqueFunctions.push(f);
stringifiedFunctions.push(f.toString);
}
}
Gotchas
VUE Реактивность
Vue.js не реагирует на наборы, поэтому вам необходимо вручную обновить компонент, который вы используете для $ forceUpdate после изменения набора
Прокси -набор
Наборы несовместимы с Proxy (), поэтому вы не можете добавить для него глобальный Getter/Setter, но вы все равно можете использовать Object.DefineProperty в нем.
Примитивные и эталонные типы
Наборы будут работать лучше с примитивными типами, такими как строка и числа, но они также могут использоваться с эталонными типами, такими как объекты и массивы, если ссылка на объект одинакова или вы делаете часть преобразования в значениях.
бывший:
const list = [];
const listItem1 = { foo: 'bar' };
const listItem2 = { foo: 'bar' };
// if you do
new Set([listItem1, listItem1]) // you will get a set with just [listItem1]
// But if you use 2 different references, even if the values are the same
new Set([listItem1, listItem2]) // you will get a set with [listItem1, listItem2];
Оригинал: “https://dev.to/rokuem/managing-and-removing-duplicated-values-with-javascript-sets-bb1”