지금까지 우리는 다음과 같은 복잡한 데이터 구조에 대해 배웠습니다.
객체는 키가 지정된 컬렉션을 저장하는 데 사용됩니다.
배열은 순서가 지정된 컬렉션을 저장하는 데 사용됩니다.
그러나 실제 생활에서는 그것만으로는 충분하지 않습니다. 그렇기 때문에 Map
과 Set
도 존재합니다.
Map은 Object
와 마찬가지로 주요 데이터 항목의 모음입니다. 그러나 주요 차이점은 Map
모든 유형의 키를 허용한다는 것입니다.
메서드와 속성은 다음과 같습니다.
new Map()
– 지도를 생성합니다.
map.set(key, value)
– 키로 값을 저장합니다.
map.get(key)
– 키로 값을 반환합니다. key
맵에 없으면 undefined
되지 않습니다.
map.has(key)
– key
존재하면 true
반환하고, 그렇지 않으면 false
반환합니다.
map.delete(key)
– 키로 요소(키/값 쌍)를 제거합니다.
map.clear()
– 지도에서 모든 것을 제거합니다.
map.size
– 현재 요소 수를 반환합니다.
예를 들어:
let map = new Map(); map.set('1', 'str1'); // 문자열 키 map.set(1, 'num1'); // 숫자 키 map.set(true, 'bool1'); // 부울 키 // 일반 객체를 기억하시나요? 키를 문자열로 변환합니다 // Map은 유형을 유지하므로 다음 두 가지는 다릅니다. 경고(map.get(1)); // '숫자1' 경고( map.get('1') ); // 'str1' 경고(map.size); // 3
보시다시피 객체와 달리 키는 문자열로 변환되지 않습니다. 모든 유형의 키가 가능합니다.
map[key]
Map
사용하는 올바른 방법이 아닙니다.
map[key]
도 작동하지만, 예를 들어 map[key] = 2
로 설정할 수 있지만 이는 map
일반 JavaScript 객체로 취급하므로 해당하는 모든 제한 사항(문자열/기호 키만 등)을 의미합니다.
그래서 우리는 map
메소드( set
, get
등)를 사용해야 합니다.
맵은 객체를 키로 사용할 수도 있습니다.
예를 들어:
let john = { 이름: "John" }; // 모든 사용자에 대해 방문 횟수를 저장해 보겠습니다. VisitsCountMap = new Map(); // John은 지도의 키입니다. VisitsCountMap.set(존, 123); 경고( VisitsCountMap.get(john) ); // 123
객체를 키로 사용하는 것은 가장 주목할 만하고 중요한 Map
기능 중 하나입니다. Object
에도 동일하게 적용되지 않습니다. Object
의 키로 String을 사용하는 것은 괜찮지만 Object
의 키로 다른 Object
사용할 수는 없습니다.
시도해 봅시다:
let john = { 이름: "John" }; let ben = { 이름: "벤" }; VisitsCountObj = {}라고 놔두세요; //객체를 사용해본다 VisitsCountObj[ben] = 234; // ben 객체를 키로 사용하려고 합니다. VisitsCountObj[존] = 123; // john 객체를 키로 사용하려고 하면 ben 객체가 대체됩니다. // 그것이 쓰여진 것입니다! 경고( VisitsCountObj["[object Object]"] ); // 123
visitsCountObj
객체이므로 위의 john
및 ben
과 같은 모든 Object
키를 동일한 문자열 "[object Object]"
로 변환합니다. 확실히 우리가 원하는 것은 아닙니다.
Map
키를 비교하는 방법
키의 동등성을 테스트하기 위해 Map
SameValueZero 알고리즘을 사용합니다. 이는 엄격한 동등성 ===
과 대략 동일하지만 차이점은 NaN
NaN
과 동일한 것으로 간주된다는 것입니다. 따라서 NaN
도 키로 사용할 수 있습니다.
이 알고리즘은 변경하거나 사용자 정의할 수 없습니다.
체인화
모든 map.set
호출은 지도 자체를 반환하므로 호출을 "연결"할 수 있습니다.
map.set('1', 'str1') .set(1, '숫자1') .set(참, 'bool1');
map
반복하는 데는 세 가지 방법이 있습니다.
map.keys()
– 키에 대한 반복 가능 항목을 반환합니다.
map.values()
– 값에 대한 반복 가능 항목을 반환합니다.
map.entries()
– 항목 [key, value]
에 대한 반복 가능 항목을 반환하며 기본적으로 for..of
에서 사용됩니다.
예를 들어:
recipeMap = new Map([ ['오이', 500], ['토마토', 350], ['양파', 50] ]); // 키(야채)를 반복합니다. for (recipeMap.keys()의 야채를 놓아두세요) { 경고(야채); // 오이, 토마토, 양파 } // 값(금액)을 반복합니다. for (recipeMap.values()의 양을 설정) { 알림(금액); // 500, 350, 50 } // [key, value] 항목을 반복합니다. for (recipeMap의 항목 허용) { // recipeMap.entries()와 동일 경고(입력); // 오이,500(등등) }
게재 신청서가 사용됩니다.
반복은 값이 삽입된 순서와 동일하게 진행됩니다. Map
일반 Object
와 달리 이 순서를 유지합니다.
그 외에도 Map
Array
와 유사한 forEach
메서드가 내장되어 있습니다.
// 각 (키, 값) 쌍에 대해 함수를 실행합니다. recipeMap.forEach( (값, 키, 맵) => { Alert(`${key}: ${value}`); // 오이: 500 등 });
Map
이 생성되면 다음과 같이 초기화를 위해 키/값 쌍이 있는 배열(또는 다른 반복 가능 항목)을 전달할 수 있습니다.
// [키, 값] 쌍의 배열 map = new Map([ ['1', 'str1'], [1, '숫자1'], [참, 'bool1'] ]); 경고( map.get('1') ); // str1
일반 객체가 있고 그로부터 Map
생성하려는 경우 객체에 대한 키/값 쌍의 배열을 정확히 해당 형식으로 반환하는 내장 메서드 Object.entries(obj)를 사용할 수 있습니다.
따라서 우리는 다음과 같은 객체로부터 지도를 만들 수 있습니다:
obj = {라고 놔두세요 이름: "존", 나이: 30 }; let map = new Map(Object.entries(obj)); 경고( map.get('이름') ); // 존
여기서 Object.entries
키/값 쌍의 배열( [ ["name","John"], ["age", 30] ]
을 반환합니다. 이것이 Map
필요한 것입니다.
우리는 방금 Object.entries(obj)
사용하여 일반 객체에서 Map
생성하는 방법을 살펴보았습니다.
그 반대를 수행하는 Object.fromEntries
메소드가 있습니다. [key, value]
쌍의 배열이 주어지면 그로부터 객체를 생성합니다.
가격 = Object.fromEntries([ ['바나나', 1], ['주황색', 2], ['고기', 4] ]); // 이제 가격 = { 바나나: 1, 오렌지: 2, 고기: 4 } 경고(가격.주황색); // 2
Object.fromEntries
사용하여 Map
에서 일반 객체를 가져올 수 있습니다.
예를 들어 데이터를 Map
에 저장하지만 일반 객체를 기대하는 타사 코드에 이를 전달해야 합니다.
여기 있습니다:
let map = new Map(); map.set('바나나', 1); map.set('주황색', 2); map.set('고기', 4); let obj = Object.fromEntries(map.entries()); // 일반 객체 만들기 (*) // 완료! // obj = { 바나나: 1, 오렌지: 2, 고기: 4 } 경고(obj.orange); // 2
map.entries()
를 호출하면 Object.fromEntries
에 대한 올바른 형식으로 정확하게 키/값 쌍의 반복 가능 항목이 반환됩니다.
(*)
줄을 더 짧게 만들 수도 있습니다.
let obj = Object.fromEntries(map); // .entries() 생략
Object.fromEntries
반복 가능한 객체를 인수로 기대하기 때문에 동일합니다. 반드시 배열일 필요는 없습니다. 그리고 map
의 표준 반복은 map.entries()
와 동일한 키/값 쌍을 반환합니다. 따라서 우리는 map
과 동일한 키/값을 가진 일반 객체를 얻습니다.
Set
은 각 값이 한 번만 발생할 수 있는 "값 집합"(키 없음)이라는 특수한 유형 컬렉션입니다.
주요 방법은 다음과 같습니다.
new Set([iterable])
– 세트를 생성하고, iterable
객체(일반적으로 배열)가 제공되면 그 값을 세트로 복사합니다.
set.add(value)
– 값을 추가하고 세트 자체를 반환합니다.
set.delete(value)
– 값을 제거하고, 호출 당시 value
존재하면 true
반환하고, 그렇지 않으면 false
반환합니다.
set.has(value)
– 값이 세트에 있으면 true
반환하고, 그렇지 않으면 false
반환합니다.
set.clear()
– 세트에서 모든 것을 제거합니다.
set.size
– 요소 수입니다.
주요 특징은 동일한 값으로 set.add(value)
를 반복적으로 호출해도 아무 작업도 수행되지 않는다는 것입니다. 이것이 바로 각 값이 Set
에 한 번만 나타나는 이유입니다.
예를 들어 방문객이 찾아오는데 모든 사람을 기억하고 싶습니다. 그러나 반복 방문으로 인해 중복 방문이 발생해서는 안 됩니다. 방문자는 한 번만 "계산"되어야 합니다.
Set
이에 딱 맞는 것입니다.
let set = new Set(); let john = { 이름: "John" }; let pete = { 이름: "피트" }; let 메리 = { 이름: "메리" }; // 방문, 일부 사용자는 여러 번 방문 set.add(존); set.add(피트); set.add(메리); set.add(존); set.add(메리); // 세트는 고유한 값만 유지합니다. 경고(set.size); // 3 for (세트 사용자에게 허용) { 경고(사용자.이름); // John(당시 Pete와 Mary) }
Set
의 대안은 사용자 배열과 arr.find를 사용하여 삽입할 때마다 중복을 확인하는 코드일 수 있습니다. 그러나 이 방법은 모든 요소를 확인하면서 전체 배열을 살펴보기 때문에 성능이 훨씬 더 나빠집니다. Set
고유성 확인을 위해 내부적으로 훨씬 더 최적화되어 있습니다.
for..of
또는 forEach
사용하여 세트를 반복할 수 있습니다.
let set = new Set(["오렌지", "사과", "바나나"]); for(설정된 값) 경고(값); // forEach와 동일: set.forEach((value, valueAgain, set) => { 경고(값); });
재미있는 점을 참고하세요. forEach
에 전달된 콜백 함수에는 value
, 동일한 값 valueAgain
, 대상 개체 등 3개의 인수가 있습니다. 실제로 동일한 값이 인수에 두 번 나타납니다.
이는 forEach
전달된 콜백에 세 개의 인수가 있는 Map
과의 호환성을 위한 것입니다. 확실히 조금 이상해 보입니다. 그러나 이는 어떤 경우에는 Map
Set
으로 쉽게 바꾸는 데 도움이 될 수 있으며 그 반대의 경우도 마찬가지입니다.
반복자에 대해 Map
과 동일한 메서드도 지원됩니다.
set.keys()
– 값에 대해 반복 가능한 객체를 반환합니다.
set.values()
– Map
과의 호환성을 위해 set.keys()
와 동일합니다.
set.entries()
– [value, value]
항목에 대해 반복 가능한 객체를 반환하며 Map
과의 호환성을 위해 존재합니다.
Map
– 키 값의 모음입니다.
방법 및 속성:
new Map([iterable])
– 초기화를 위한 [key,value]
쌍의 선택적 iterable
(예: 배열)을 사용하여 맵을 생성합니다.
map.set(key, value)
– 키로 값을 저장하고 맵 자체를 반환합니다.
map.get(key)
– 키로 값을 반환합니다. key
맵에 없으면 undefined
되지 않습니다.
map.has(key)
– key
존재하면 true
반환하고, 그렇지 않으면 false
반환합니다.
map.delete(key)
– 키로 요소를 제거하고, 호출 시 key
존재하면 true
반환하고, 그렇지 않으면 false
반환합니다.
map.clear()
– 지도에서 모든 것을 제거합니다.
map.size
– 현재 요소 수를 반환합니다.
일반 Object
와의 차이점은 다음과 같습니다.
모든 키, 객체는 키가 될 수 있습니다.
추가적인 편리한 방법인 size
속성입니다.
Set
– 고유한 값의 모음입니다.
방법 및 속성:
new Set([iterable])
– 초기화를 위한 값의 선택적 iterable
(예: 배열)을 사용하여 세트를 생성합니다.
set.add(value)
– 값을 추가하고( value
존재하는 경우 아무 작업도 수행하지 않음) 세트 자체를 반환합니다.
set.delete(value)
– 값을 제거하고, 호출 당시 value
존재하면 true
반환하고, 그렇지 않으면 false
반환합니다.
set.has(value)
– 값이 세트에 있으면 true
반환하고, 그렇지 않으면 false
반환합니다.
set.clear()
– 세트에서 모든 것을 제거합니다.
set.size
– 요소 수입니다.
Map
및 Set
에 대한 반복은 항상 삽입 순서이므로 이러한 컬렉션이 순서가 없다고 말할 수는 없지만 요소의 순서를 바꾸거나 번호로 요소를 직접 가져올 수는 없습니다.
중요도: 5
arr
배열로 둡니다.
arr
의 고유 항목이 있는 배열을 반환해야 하는 함수 unique(arr)
를 만듭니다.
예를 들어:
함수 고유(arr) { /* 귀하의 코드 */ } 값 = ["토끼", "크리슈나", "토끼", "크리슈나", "크리슈나", "크리슈나", "토끼", "토끼", ":-O" ]; 경고(고유(값)); // 토끼, 크리슈나, :-O
PS 여기서는 문자열이 사용되지만 모든 유형의 값이 될 수 있습니다.
PPS Set
사용하여 고유한 값을 저장합니다.
테스트를 통해 샌드박스를 엽니다.
함수 고유(arr) { return Array.from(new Set(arr)); }
샌드박스에서 테스트를 통해 솔루션을 엽니다.
중요도: 4
철자 바꾸기(anagrams)는 같은 수의 같은 문자를 갖지만 순서가 다른 단어입니다.
예를 들어:
낮잠 - 팬 귀 - 아르 - 시대 사기꾼 - 헥타르 - 교사
철자 바꾸기에서 정리된 배열을 반환하는 함수 aclean(arr)
을 작성하세요.
예를 들어:
let arr = ["nap", "선생님", "사기꾼", "PAN", "귀", "시대", "헥타르"]; 경고( aclean(arr) ); // "nap,teachers,ear" 또는 "PAN,cheaters,era"
모든 철자 바꾸기 그룹에서는 어느 단어든 한 단어만 남겨야 합니다.
테스트를 통해 샌드박스를 엽니다.
모든 철자 바꾸기를 찾으려면 모든 단어를 문자로 나누고 정렬해 봅시다. 문자로 정렬하면 모든 철자 바꾸기가 동일합니다.
예를 들어:
낮잠, 팬 -> anp 귀, 시대, 아르 -> 에어 사기꾼, 헥타르, 교사 -> aceehst ...
각 키당 하나의 값만 저장하기 위해 문자 정렬 변형을 맵 키로 사용합니다.
함수 aclean(arr) { let map = new Map(); for (arr의 단어를 보자) { // 단어를 문자별로 분할하고 정렬한 후 다시 결합합니다. let sorted = word.toLowerCase().split('').sort().join(''); // (*) map.set(정렬, 단어); } return Array.from(map.values()); } let arr = ["nap", "선생님", "사기꾼", "PAN", "귀", "시대", "헥타르"]; 경고( aclean(arr) );
문자 정렬은 (*)
행의 호출 체인에 의해 수행됩니다.
편의상 여러 줄로 나누어 보겠습니다.
정렬 = 단어 // PAN .toLowerCase() // 팬 .split('') // ['p','a','n'] .sort() // ['a','n','p'] .가입하다(''); // 앤프
두 개의 다른 단어 'PAN'
과 'nap'
은 동일한 문자 정렬 형식 'anp'
받습니다.
다음 줄에서는 단어를 맵에 넣습니다.
map.set(정렬, 단어);
동일한 문자로 정렬된 형태의 단어를 다시 만나면 맵에서 동일한 키로 이전 값을 덮어쓰게 됩니다. 따라서 우리는 항상 문자 형식당 최대 한 단어를 갖게 됩니다.
마지막에 Array.from(map.values())
맵 값에 대한 반복 가능 항목을 취하고(결과에는 키가 필요하지 않습니다) 그 배열을 반환합니다.
여기서는 키가 문자열이기 때문에 Map
대신 일반 객체를 사용할 수도 있습니다.
이것이 솔루션의 모습입니다.
함수 aclean(arr) { obj = {}로 놔두세요; for (let i = 0; i < arr.length; i++) { let sorted = arr[i].toLowerCase().split("").sort().join(""); obj[sorted] = arr[i]; } return Object.values(obj); } let arr = ["nap", "선생님", "사기꾼", "PAN", "귀", "시대", "헥타르"]; 경고( aclean(arr) );
샌드박스에서 테스트를 통해 솔루션을 엽니다.
중요도: 5
변수에서 map.keys()
배열을 가져온 다음 여기에 배열별 메서드(예: .push
)를 적용하려고 합니다.
하지만 그건 작동하지 않습니다.
let map = new Map(); map.set("이름", "John"); 키 = map.keys(); // 오류: key.push는 함수가 아닙니다. key.push("더보기");
왜? keys.push
작동하도록 코드를 어떻게 수정할 수 있나요?
이는 map.keys()
반복 가능한 항목을 반환하지만 배열은 반환하지 않기 때문입니다.
Array.from
사용하여 이를 배열로 변환할 수 있습니다.
let map = new Map(); map.set("이름", "John"); 키 = Array.from(map.keys()); key.push("더보기"); 경고(키); // 이름, 기타