Set - garevna/js-course GitHub Wiki
Конструктор
Создает итерабельный объект с уникальными элементами
из итерабельного объекта
const sample = new Set ( [ 5, 1, 3, 8, 1, 5, 3, 3, 8 ] )
console.log ( sample )
// ► Set(4) {5, 1, 3, 8}
console.log ( Array.from ( sample ) )
// ► (4) [5, 1, 3, 8]Давайте посмотрим, на что он способен:
▼ Set {constructor: ƒ, has: ƒ, add: ƒ, delete: ƒ, clear: ƒ, …}
► add: ƒ add()
► clear: ƒ clear()
► constructor: ƒ Set()
► delete: ƒ delete()
► entries: ƒ entries()
► forEach: ƒ forEach()
► has: ƒ has()
► keys: ƒ values()
size: (...)
► values: ƒ values()
► Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.toStringTag): "Set"
► get size: ƒ size()
► __proto__: Objectsample.values() создает и возвращает объект итератора
Создадим итератор и будем вызывать его метод next() в цикле:
let done, iterator = sample.values()
while ( !done ) {
( { value: item, done } = iterator.next() );
console.log ( item )
}В консоль будут выведены все значения, находящиеся в объекте sample
Добавляет новое значение в объект, если его там нет
Если такое значение уже существует, то ничего не изменится
sample.add ( 1 )
console.log ( sample )
// ► Set(4) {5, 1, 3, 8}
sample.add ( 7 )
console.log ( sample )
// ► Set(4) {5, 1, 3, 8, 7}Удаляет значение, если оно есть в объекте, и возвращает true
Если такого значения нет, то вернет false
sample.delete ( 9 )
// false
sample.delete ( 7 )
// true
console.log ( sample )
// ► Set(4) {5, 1, 3, 8}Мы знаем, что массивы и объекты передаются по ссылке
Поэтому userSet будет содержать все объекты из массива users,
хотя содержимое объектов дублируется
const users = [
{
name: "Stephan",
job: "java-developer"
},
{
name: "Margaret",
job: "markup-developer"
},
{
name: "Stephan",
job: "java-developer"
},
{
name: "Margaret",
job: "markup-developer"
}
]
const userSet = new Set ( users )
console.log ( userSet )Однако если мы сделаем так:
const user1 = {
name: "Stephan",
job: "java-developer"
}
const user2 = {
name: "Margaret",
job: "markup-developer"
}
const usersDoubled = [
user1, user2, user1, user1, user2
]
const usersNewSet = new Set ( usersDoubled )
console.log ( usersNewSet )то дублирующиеся ссылки будут исключены
Создадим итерабельный объект human и убедимся, что дублирующиеся значения будут исключены:
const human = {
a: "Alex",
b: "Mary",
c: "Helen",
d: "Nicolas",
e: "Jeck",
f: "Henry",
i: "Mary",
g: "Alex"
}
human[Symbol.iterator] = function* () {
for ( let prop in this )
yield this [prop]
}
const humanSet = new Set ( human )
console.log ( humanSet )▼ Set(6) {"Alex", "Mary", "Helen", "Nicolas", "Jeck", …}
size: 6
► __proto__: Set
▼ [[Entries]]: Array(6)
▼ 0: "Alex"
value: "Alex"
► 1: "Mary"
► 2: "Helen"
► 3: "Nicolas"
► 4: "Jeck"
► 5: "Henry"
length: 6
которая будет создавать структуру данных с уникальными элементами из переданного ей "материала"
function createSet ( object = {} ) {
function createIterator () {
object[Symbol.iterator] = function* () {
for ( let prop in this ) yield this [prop]
}
}
Array.isArray ( object ) || object[Symbol.iterator] ?
null : createIterator()
return new Set ( object )
}Теперь опробуем ее в действии:
console.log (
createSet ( { a: 1, b: 4, c:5, d: 4, e: 5 } )
)
// ► Set(3) {1, 4, 5}
console.log ( createSet () )
// ► Set(0) {}
console.log (
createSet ( [ 5, 5, 4, 7, 8, 4, 7, 9, 8 ] )
)
// ► Set(5) {5, 4, 7, 8, 9}
console.log ( createSet ( "A9h4G8=45*hG///19*74-78" ) )
// ▼ Set(13) {"A", "9", "h", "4", "G", …}
// size: (...)
// ► __proto__: Set
// ▼ [[Entries]]: Array(13)
// ► 0: "A"
// ► 1: "9"
// ► 2: "h"
// ► 3: "4"
// ► 4: "G"
// ► 5: "8"
// ► 6: "="
// ► 7: "5"
// ► 8: "*"
// ► 9: "/"
// ► 10: "1"
// ► 11: "7"
// ► 12: "-"
// length: 13Если же мы хотим оставить только уникальные символы в строке, то можно так:
console.log (
Array.from (
createSet ( "A9h4G8=45*hG///19*74-78" )
).join ("")
)
// A9h4G8=5*/17-Но остается вопрос, как заставить работать Set с объектами в качестве элементов структуры данных
Поиграем с массивом объектов clients
const clients = [
{
name: "Stephan",
job: "java-developer"
},
{
name: "Margaret",
job: "markup-developer"
},
{
name: "Stephan",
job: "java-developer"
},
{
name: "Margaret",
job: "markup-developer"
}
]Напилим функцию, которая возвращает нам структуру данных с уникальными клиентами из массива clients, но сохраненными в виде JSON-строки:
function createSetOfObjects ( object = {} ) {
if ( !object || ( typeof object !== "object" && typeof object !== "string" ) )
return new Set ( {} )
object[Symbol.iterator] = function* () {
for ( let prop in this )
yield JSON.stringify ( this [prop] )
}
return new Set ( object )
}
let uniqueClients = createSetOfObjects ( clients )
console.log ( uniqueClients )Результат:
► Set(2) {"{"name":"Stephan","job":"java-developer"}", "{"name":"Margaret","job":"markup-developer"}"}Попробуем добавить запись:
uniqueClients.add ( JSON.stringify ( clients[0] ) )Поскольку такая запись уже существует, добавление не произошло
Давайте выведем записи:
for ( let item of uniqueClients )
console.log ( JSON.parse ( item ) )
// ► {name: "Stephan", job: "java-developer"}
// ► {name: "Margaret", job: "markup-developer"}Все работает, но, очевидно, нужно писать "обертку" для нашего объекта,
чтобы при добавлении новых объектов они сначала превращались в JSON-строку,
а при итерировании объекта возвращались распарсенные значения
class UnicElements {
constructor ( object = {} ) {
if ( !object || ( typeof object !== "object" && typeof object !== "string" ) )
return new Set ( {} )
object[Symbol.iterator] = function* () {
for ( let prop in this )
yield JSON.stringify ( this [prop] )
}
this.data = new Set ( object )
this.data[Symbol.iterator] = this.data.values()
}
next () {
let current = this.data[Symbol.iterator].next()
return current.done ? null : JSON.parse ( current.value )
}
add ( newElem ) {
this.data.add ( JSON.stringify ( newElem ) )
this.data[Symbol.iterator] = this.data.values()
}
delete ( elem ) {
this.data.delete ( JSON.stringify ( elem ) )
this.data[Symbol.iterator] = this.data.values()
}
}
const testObject = new UnicElements ( clients )Теперь опробуем, как работает наш декорированный экземпляр testObject
Сначала посмотрим на работу итератора:
testObject.next()
// {name: "Stephan", job: "java-developer"}
testObject.next()
// {name: "Margaret", job: "markup-developer"}
testObject.next()
// nullВсе, дошли до конца
Теперь посмотрим, как добавляются новые объекты
Попробуем добавить один и тот же объект несколько раз:
testObject.add({name:"Google", job: "developer"})
testObject.add({name:"Google", job: "developer"})
testObject.add({name:"Google", job: "developer"})
testObject.next()
// {name: "Stephan", job: "java-developer"}
testObject.next()
// {name: "Margaret", job: "markup-developer"}
testObject.next()
// {name: "Google", job: "developer"}
testObject.next()
// nullВсе норм, объект добавился только один раз
Теперь посмотрим на удаление объектов:
testObject.delete({name:"Google", job: "developer"})
testObject.next()
// {name: "Stephan", job: "java-developer"}
testObject.next()
// {name: "Margaret", job: "markup-developer"}
testObject.next()
// nullВсе в порядке, объект благополучно удален 
Set
new Set