코딩/Javascript

[JS-중급편-OOP] 14. Set 알아보기

드리프트 2021. 1. 4. 00:03
728x170

 

 

이번 시간에는 세트(Set)에 대해 알아 보겠습니다.

 

여러 데이터를 저장할 때 아마 배열이 가장 먼저 떠오를 것입니다.

 

배열은 꽤 오랫동안 사용되어져 왔고 또한 매우 유연하며 사용하기 쉽게 만드는 많은 특성이 있습니다.

 

지난 몇 년 동안 JavaScript는 데이터 모음을 저장하는 또 다른 방법을 얻었습니다.

 

그 방법은 세트(Set)로 알려진 배열 모양의 방법을 통하는 것입니다.

 

표면적으로 배열과 세트는 매우 비슷해 보입니다.

 

그러나 세트는 배열과 다르게 보이는 많은 특성을 가지고 있습니다.

 

가장 큰 특징은 중복 값의 저장 허용 여부입니다.

 

세트에는 유니크한 아이템만 저장할 수 있습니다.

 

우리가 원하는 것을 저장하기 위해 세트를 사용할 수 있습니다.

 

그러나 배열과는 달리 우리는 그 항목을 한 번만 저장할 수 있습니다.

 

이미 세트의 일부인 항목의 복제본을 추가하려고 하면 해당 항목이 무시되고 컬렉션에 추가되지 않습니다.

 

세트(Set)에 대해 좀 더 자세히 알아보겠습니다.

 

 

Set 만들기

세트를 사용하려면 먼저 세트를 만들어야합니다.

 

어려운 건 없습니다.

 

세트를 만들 수 있는 유일한 방법은 Set 생성자를 호출하는 것입니다.

 

let mySet = new Set();

 

이 코드가 실행되면 mySet이라는 빈 Set 객체가 생성됩니다.

new Set()를 입력하는 것 외에 세트를 만드는 다른 방법이 있는지 궁금 할 것입니다.

 

대답은 '아니요'입니다.

 

 

 

세트에 아이템 추가하기

세트가 있으면 우리는 add 메소드를 사용하여 항목을 추가 할 수 있습니다.

 

let mySet = new Set();
mySet.add("blarg");
mySet.add(10);
mySet.add(true);

 

위 코드에서는 mySet이라는 세트 객체에 텍스트 값 blarg, 숫자 10, 부울린 true를 넣었습니다.

 

세트에 이미 존재하는 항목을 새로 추가하려고 하면 그 새 항목은 추가되지 않습니다.

 

다음 코드의 마지막 라인을 보십시요.

 

let mySet = new Set();
mySet.add("blarg");
mySet.add(10);
mySet.add(true);
mySet.add("blarg") // rut roh

 

세트에 blarg 텍스트를 한 번 더 추가하려고 합니다.

 

blarg 항목이 이미 존재하므로 세트에서 이 중복 항목을 한 번 더 추가하지 않습니다.

 

항목이 중복되지 않는 것은 배열과 비교할 때 Set의 가장 강력한 차별화된 요소 중 하나입니다.

 

세트가 중복 된 항목을 만나면 무시하고 나머지 코드도 전혀 아무 일도 없었다는 듯이 행동합니다.

 

 

 

세트는 어떻게 중복을 확인할까요?

 

우리가 Set 객체에 아이템을 추가할 때, Set 객체는 추가하는 항목이 이미 세트에 있는 다른 항목과 동일한 지 여부를 확인하는 매우 빠른 방법을 가지고 있습니다.

 

세트가 다른 항목과의 동등성을 확인하는 방법은 엄격한 동등연산자 (===) 접근 방식을 사용하는 것입니다.

 

===를 사용하면 일단 세트에서 프리미티브 값과 객체 참조가 같은지 확인합니다.

 

프리미티브 값 부분은 위 예제에서 보듯이 텍스트, 숫자 및 부울린을 추가 한 것입니다.

 

다음 코드를 한번 보십시요.

 

let sayWhat = new Set();
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");
sayWhat.add("Lobby!");

console.log(sayWhat); // Lobby!

 

다음 예를 살펴보시면 조금 더 흥미로운 부분이 있을겁니다.

 

let anotherSet = new Set();
anotherSet.add(true);
anotherSet.add("abc");
anotherSet.add([1, 2]);
anotherSet.add([1, 2]);

 

anotherSet 개체의 콘텐츠가 어떻게 될 것이라고 생각하십니까?

 

답은 true, "abc", [1, 2], [1, 2]입니다.

 

이상하게 보일 수있는 부분은 우리가 추가한 두 개의 [1, 2] 배열입니다.

 

우리 인간에게는 이 두 배열이 똑같이 보입니다.

 

세트가 동등성을 확인할 때 쓰는 === 연산자로 인해 세트에서는 두 개의 [1,2] 배열이 서로 구별됩니다.

 

세트가 동등하다고 선언하는 것은 객체 참조가 동일한 것을 참조 할 때입니다.

 

let myArray = [1, 2];
let anotherSet = new Set();

anotherSet.add(true);
anotherSet.add("abc");
anotherSet.add(myArray);
anotherSet.add(myArray);

 

이 경우 1과 2의 배열 값을 저장하는 myArray 객체가 있습니다.

 

이 객체는 이제 세트에 두 번 추가하고 있는데 두 개의 myArray 객체 참조를 추가하므로 === 연산자는 다음과 같이 말합니다.

 

둘 다 동일하다고요. 최종 결과는 세트 내에서 myArray 배열은 한 번만 저장됩니다.

 

이 상황에서 anotherSet의 최종 내역은 true, "abc" 및 [1, 2]입니다.

 

 

 

세트 만들기 2

 

위에서 add 메서드로 빈 세트에 항목을 추가하는 방법을 알아 봤습니다.

 

세트를 만들 수 있는 또 다른 방법이 있습니다.

 

new 키워드를 쓰는 건 똑같지만 이번에는 세트를 만들 때 기존 데이터 컬렉션을 전달할 수 있습니다.

 

let someValues = ["a", "b", "c", 10, "a", "c", false];
let newSet = new Set(someValues);
console.log(newSet); // "a", "b", "c", 10, false

 

이 스니펫(snippet)에는 someValues 배열이 있으며, "a"와 "c" 같은 일부 항목이 중복됩니다.

 

newSet 객체를 만들 때 여전히 new Set() 표현식을 사용하지만 someValues 배열을 Set 생성자에 전달합니다.

 

그러면 중복 항목이 필터링 되어 새로 만들어진 newSet 세트 객체에는 전달한 항목의 고유한 값만 남습니다.

 

이것은 또 다른 질문을 제기 할 수 있습니다.

 

세트를 만들 때 어떤 종류의 컬렉션을 세트 생성자에게 전달할 수 있나요?

 

답은 이터러벌(iterable) 객체입니다.

 

이터러벌(iterable) 객체는 모든 값을 순환하는 방법을 제공한다는 뜻입니다.

 

배열은 그러한 객체의 한 예입니다.

 

Text(Strings), TypedArrays, Maps, 기타 Set 객체, NodeList 등이 여기에 해당됩니다.

 

이 글을 마무리하기 전에 세트 생성의 일부로 문자열 (일명 이터러블(iterable) 객체!)을 전달하는 방법을 살펴 보겠습니다.

 

let textSet = new Set("diplodocus");
console.log(textSet); // d, i, p, l, o, c, u, s

 

"diplodocus"라는 단어를 전달하면 최종적으로 세트에 저장되는 것은 중복되지 않은 고유한 캐릭터입니다.

 

각 문자는 세트의 개별 항목이 됩니다.

 

반복 가능한(이터러블-iterable) 객체가 전달 될 때마다 해당 객체의 각 개별 값은 고유성을 평가하고 해당 값이 실제로 고유한 경우 세트에 추가됩니다.

 

 

세트 객체 사이즈 확인하기

세트에 얼마나 많은 항목이 있는지 파악하기 위해  size 속성을 사용할 수 있습니다.

 

let setCount = new Set();
console.log(setCount.size); // 0

setCount.add("foo");
console.log(setCount.size); // 1

setCount.add("bar");
setCount.add("zorb");
console.log(setCount.size); // 3

 

size 속성에서 반환 된 값은 세트에서 항목을 추가하거나 제거 할 때마다 업데이트됩니다.

 

 

 

세트에서 항목 삭제하기

세트에서 항목을 삭제하거나 제거하려면 delete 메서드를 사용하고 그 인수에 제거하려는 항목의 값을 전달하면 됩니다.

 

var robotSounds = new Set(["beep", "boop", "who dis?"]);
robotSounds.delete("who dis?");

console.log(robotSounds) // "beep", "boop"

 

항목을 삭제하면 삭제된 항목이 세트에서 제거되고 true 값이 반환됩니다.

let robotSounds = new Set(["beep", "boop", "who dis?"]);

if (robotSounds.delete("who dis?")) {
  console.log("Item successfully deleted!");
}

console.log(robotSounds) // "beep", "boop"

 

존재하지 않는 항목을 삭제하려고 하면 세트는 변경되지 않고 대신 delete 메서드는 false를 반환합니다.

 

항목을 개별적으로 삭제하는 것이 편리하지만 세트에서 모든 항목을 완전히 비우고 싶을 때가 있을겁니다.

 

clear 방법을 사용하면 됩니다.

 

let vegetables = new Set(["🥑","🌶️", "🥦", "🥓", "🥔"]);
console.log(vegetables.size); // 5

vegetables.clear();
console.log(vegetables.size); // 0

 

세트에서 모든 항목을 지우는 또 다른 방법은 new Set()를 수행하여 Set 객체를 다시 만드는 것입니다.

 

그러나 실제로는 new Set() 방법은 clear 메서드에 비해 빠르지 않기 때문에 되도록이면 clear 메서드를 사용하는 게 좋습니다.

 

 

세트에 항목이 있는지 없는지 확인하는 방법

세트는 중복을 확인하는데 정말 빠를 뿐만 아니라 처음에 컬렉션에 항목이 있는지 확인하는데도 정말 빠릅니다.

 

항목이 있는지 없는지 확인하려면 has 메소드를 사용할 수 있습니다.

 

let ingredients = new Set(["milk", "eggs", "cheese", "tofu"]);

if (ingredients.has("tofu")) {
  ingredients.delete("tofu");
  ingredients.add("bacon");
}

console.log(ingredients); // "milk", "eggs", "cheese", "bacon"

 

has 메소드는 확인하려는 항목을 인수로 사용합니다. 항목이 있으면 true를 반환합니다.

 

컬렉션에 항목이 없으면 false를 반환합니다.

 

여기서 중복 식별 작동 방식은 엄격한 동등성 연산자(===)를 이용하는 겁니다.

 

 

 

세트에서 Loop 하기

세트의 항목을 반복해야 할 때가 있습니다.

 

이를 수행 할 수 있는 방법은 for ... of 루핑 패턴을 사용하는 것입니다.

 

다음 예를 살펴보십시오.

 

let textSet = new Set("diplodocus");

for (let letter of textSet) {
  console.log(letter);
}

 

이 for 루프는 세트의 모든 항목에 도달 할 때까지 실행됩니다.

 

세트의 항목에 액세스하는 순서는 처음에 세트에 추가된 순서와 동일합니다.

 

배열과 달리 세트에는 반복 할 수 있는 인덱스 위치 개념이 없습니다.

 

 

Entries, Keys 그리고 Values

세트는 내부적으로는 키/값 쌍의 형태로 항목을 설정합니다.

 

시각화 해보면 쉽게 이해 할 수 있을 겁니다. 다음 코드가 있다고 가정해 보겠습니다.

 

let animaniacs = new Set(["Yakko", "Wakko", "Dot"]);

 

"animaniacs" 세트 내에서 Yakko, Wakko, Dot 항목이 있는 배열을 저장하고 있습니다.

 

 

 

세트의 내부가 데이터베이스 또는 두 개의 열이 있는 스프레드시트라고 생각해 봅시다.

 

한 열에는 키라는 라벨이 있고 다른 열에는 값이라는 라벨이 있습니다.

 

각 행은 저장하려는 항목을 나타냅니다.

 

해시 테이블 같은 다른 키 / 값 저장 배열과 비교할 때 세트는 키와 값 모두 동일한 데이터를 저장한다는 것입니다.

 

우리의 예에서 Yakko, Wacko 및 Dot이 키열과 값열에 모두 나타나는 이유입니다.

 

이 키와 값을 살펴본 이유는 Set 개체가 모든 키, 값, 실제 키를 반환하는 몇 가지 메서드를 제공하기 때문입니다.

 

let animaniacs = new Set(["Yakko", "Wakko", "Dot"]);

console.log(animaniacs.keys());
console.log(animaniacs.values());
console.log(animaniacs.entries());

 

 

위 코드에서 보듯이 이 메서드 이름은 반환할 데이터 유형을 명확히 하는 데 도움이 됩니다.

 

keys() 메소드는 모든 키를 반환하고, values() 메소드는 모든 값을 반환하고, entries() 메소드는 세트에 있는 모든 키/값 쌍을 반환합니다.

 

데이터가 반환되는 방식은 배열이나 일반 객체와 같은 형태가 아닙니다.

 

데이터는 Iterator 개체의 형식으로 반환됩니다.

 

즉, 항목에 액세스 할 수 있는 방법은 이전에 보았던 유사한 for ... of 접근 방식을 사용하는 것입니다.

 

for (let item of animaniacs.keys()) {
  console.log(item); // "Yakko", "Wakko", "Dot"
}

 

결론

Set 객체와 사용하게 될 다양한 속성 및 메서드에 대해 알아야 할 거의 모든 것을 배웠습니다.

 

세트를 정말 유용하게 만드는 것은 중복 항목을 감지하고 고유한 값만 저장하도록하는게 번개처럼 빠르게 이루어 지는 겁니다.

 

간단하고 단순하지 않은 다양한 데이터 관련 작업을 위해 점점 더 세트에 의존하게 될 것입니다.

 

그리드형