깊은바다거북
개발 공부 기록
깊은바다거북
전체 방문자
오늘
어제
  • 분류 전체보기 (219)
    • JAVA (9)
    • JavaScript (15)
    • 스파르타코딩클럽 (11)
      • [내일배움단] 웹개발 종합반 개발일지 (5)
      • [내일배움캠프] 프로젝트와 트러블 슈팅 (6)
    • SQL | NoSQL (4)
    • CS 등등 (0)
    • TIL | WIL (173)
    • 기타 에러 해결 (3)
    • 내 살 길 궁리 (4)

인기 글

최근 글

최근 댓글

태그

  • Linked List
  • 재귀 함수
  • Binary Tree(이진 트리)
  • 자잘한 에러 해결
  • 트러블 슈팅 Troubleshooting
  • Inorder Traversal(중위 순회)
  • DFS(깊이우선탐색)
  • TIT (Today I Troubleshot)
  • Trie
  • tree
  • TypeScript
  • 팀 프로젝트
  • 최소 힙(Min Heap)
  • BST(이진 탐색 트리)
  • BFS(너비우선탐색)
  • 시간 복잡도
  • 프로그래머스
  • Backtracking(백트래킹)
  • 자바스크립트 기초 문법
  • 최대 힙(Max Heap)
  • 자료 구조
  • 코딩테스트 연습문제
  • Til
  • POST / GET 요청
  • 점화식(Recurrence Relation)
  • Preorder Traversal(전위 순회)
  • Leetcode
  • 혼자 공부하는 자바스크립트
  • 01. 미니 프로젝트
  • leetcode-cli
hELLO · Designed By 정상우.
깊은바다거북

개발 공부 기록

4/24 월 [프로그래머스] 해시(Hash) 문제와 이터러블(Iterable) TIL
TIL | WIL

4/24 월 [프로그래머스] 해시(Hash) 문제와 이터러블(Iterable) TIL

2023. 4. 24. 15:01

[프로그래머스] 해시 > 의상

https://school.programmers.co.kr/learn/courses/30/lessons/42578

나의 풀이:

// 더 깔끔히 정리한 버전
function solution3(clothes) {
	const map = {}
	for (const [name, type] of clothes) {
		map[type] = (map[type] ?? 0) + 1;
		// map[type] = (map[type] || 0) + 1; // -> 이것도 된다. 
	}
	
	let count = 1;
	for (const key in map) {
		count *= (map[key] + 1);
	}

	return count - 1;
}

// Map을 이용하기
function solution4(clothes) {
	const map = new Map();
	for (const [name, type] of clothes) {
		map.set(type, (map.get(type) ?? 0) + 1);
	}

	let count = 1;
	map.forEach((value) => count *= value + 1)

	return count - 1;
}

테스트 코드:

// 
const { solution } = require('./201-해시-의상');

describe('Clothes (Hash)', () => {
	[
		{ clothes: [['yellow_hat', 'headgear']], result: 1 },

		// 전부 같은 종류
		{ clothes: [['yellow_hat', 'headgear'], ["green_turban", "headgear"]], result: 2 },
		{ clothes: [["crow_mask", "face"], ["blue_sunglasses", "face"], ["smoky_makeup", "face"]], result: 3 },

		// 전부 다른 종류인데 하나씩
		{ clothes: [["crow_mask", "face"], ["blue_sunglasses", "eyewear"], ["green_turban", "headgear"]], result: 7 },

		// 2개, 1개
		{ clothes: [['yellow_hat', 'headgear'], ["green_turban", "headgear"], ["crow_mask", "face"], ], result: 5 },

		// 2개, 1개, 1개
		{ clothes: [['yellow_hat', 'headgear'], ["green_turban", "headgear"], ["smoky_makeup", "face"], ["blue_sunglasses", "eyewear"], ], result: 11 },
		// // 2개, 2개, 1개
		{ clothes: [['yellow_hat', 'headgear'], ["green_turban", "headgear"], ["crow_mask", "face"], ["smoky_makeup", "face"], ["blue_sunglasses", "eyewear"], ], result: 17 },
		
	]
	.forEach(({ clothes, result }) => {
		it(`should return ${result} when given array is ${clothes}`, () => {
			expect(solution(clothes)).toBe(result);
		})
	})
})

알게된 것:

  • Map에 대하여
  • 이터러블(Iterable) 객체에 대하여 (→ Map 객체는 이터러블이다)
  • 종류별로 나눌 수 있는 상태들을 조합할 땐 ‘없다’까지 한 상태로 쳐서 곱해주면 된다.

Map에 대하여

: 키와 값의 쌍으로 이루어진 컬렉션.

  • Map의 메소드: size, has(), set(), get(), forEach(), keys(), values(), entries(), delete(), clear()
  • 객체와 유사하나 차이점은,
객체Map 객체
키로 사용할 수 있는 값문자열 또는 심벌 값객체를 포함한 모든 값
이터러블XO
요소 개수 확인Object.keys(obj).lengthmap.size
  • Map 객체는 이터러블이다. (따라서 for …of 문으로 순회가 가능하고 스프레드 문법과 배열 디스트럭처링 할당의 대상이 될 수 있음)

이터러블 (Iterable)

: 이터러블 프로토콜을 준수한 객체.

: Symbol.iterator를 프로퍼티 키로 사용하는 메소드를 가지고 있는 객체. 이는 직접 구현하거나 프로토타입 체인을 통해 상속받을 수 있다.

  • (참고) Symbol.iterator 프로퍼티 직접 구현 예시 (= 사용자 정의 이터러블 만들기):
    // 피보나치 수열을 구현한 사용자 정의 이터러블
    
    const fibonacci = {
    	// Symbol.iterator 메소드를 구현하여 이터러블 프로토콜을 준수한다. 
    	[Symbol.iterator]() {
    		let [pre, cur] = [0, 1];
    		const max = 10;
    
    		// Symbol.iterator 메소드는 next 메소드르 ㄹ소유한 이터레이터를 반환해야 하고
    		// next 메소드는 이터레이터 리절트 객체를 반환해야 한다: 
    		return {
    			next() {
    				[pre, cur] = [cur, pre + cur];
    				return { value: cur, done: cur >= max };
    			}
    		};
    	}
    };
    
    console.log([...fibonacci]) // [ 1, 2, 3, 5, 8 ]

  • 이터러블인지 확인하는 함수 만들기:
    // v가 null이 아니고 Symbol.iterator 프로퍼티가 (존재하고) 함수인가를 체크
    const isIterable = v => v !== null && typeof v[Symbol.iterator] === 'function'
    
    isIterable([]); // true
    isIterable(''); // true
    isIterable({}); // false
    
    // 혹은 이렇게도 체크해볼 수 있다:
    const array = [1, 2, 3];
    console.log(Symbole.iterator in array); // true
  • 표준 빌트인 객체 중 이터러블인 것들: 배열, 문자열, Map, Set, TypedArray, arguments, DOM 컬렉션(NodeList, HTMLCollection)
  • 이터러블 객체가 할 수 있는 일
    1. for … of 문으로 순회할 수 있다.
    1. 스프레드 문법의 대상이 된다.
    1. 배열 디스트럭처링 할당의 대상이 된다.
    const array = [1, 2, 3];
    
    // 1. for ... of 문으로 순회 가능
    for (const item ofo array) {
    	console.log(item);
    }
    
    // 2. 스프레드 문법의 대상으로 사용 가능
    console.log([...array]) // [1, 2, 3]
    
    // 3. 배열 디스트럭처링 할당의 대상으로 사용 가능
    const [a, ...rest] = array;
    console.log(a, rest); // 1, [2, 3]

이터레이터 (Iterator)

: 이터러블의 Symbole.iterator 메소드를 호출하면 반환받는 객체.

: 한 마디로, next 메소드를 갖는 객체. next 메소드는 { value: OO, done: XX } 형식의 이터레이터 리절트 객체를 반환하면 된다.

유사 배열 객체

: (이터러블이 아닌) 일반 객체인데 length 프로퍼티를 가지고 있고 키들이 숫자 형식의 문자열인 것. 그래서 length 프로퍼티를 이용해 for 문으로 순회할 수 있고 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있는 객체를 말한다.

  • 예시:
    // 유사 배열 객체
    const arrayLike = {
    	0: 1,
    	1: 2, 
    	2: 3,
    	length: 3
    }; 

즉, for 문으로 순회 가능하고 인덱스로 프로퍼티 값에 접근할 수 있어서 배열과 매우 유사하지만 Symbol.iterator 메소드가 없어서 이터러블은 아닌, 그래서 for … of 문으로 순회가 불가능한 (그리고 스프레드 문법과 배열 디스트럭처링 사용이 불가능한) 객체이다.

  • arguments, NodeList, HTMLCollection은 원래 유사 배열 객체였는데 ES6 이후 Symbol.iterator 메소드를 구현하여 이터러블이 됨.
  • 배열 자체도 ES6 이후 Symbol.iterator 메소드를 구현하여 이터러블이 됨.
  • 유사 배열 객체 또는 이터러블을 배열로 변환하고 싶을 땐(일단 이터러블이 아닌 유사 배열 객체를 이터러블로 만들 수 있다는 장점이 있으므로) :
    • Array.from() 사용하기.
      // 유사 배열 객체
      const arrayLike = {
      	0: 1,
      	1: 2, 
      	2: 3,
      	length: 3
      }; 
      
      const arr = Array.from(arrayLike);
      console.log(arr); // [1, 2, 3]

정리: 이터러블과 이터레이터

이터러블은 Symbol.iterator 메소드를 가지고 있는 객체이다. Symbol.iterator()는 이터레이터를 반환하면 된다.

이터레이터는 next 메소드를 가지고 있는 객체이다. next 메소드는 이터레이터 리절트 객체를 반환하면 된다.

따라서 Symbol.iterator 메소드와 next 메소드를 둘 다 가지도록 만들면 이터러블이면서 이터레이터인 객체가 된다. next를 명시적으로 호출할 수 있는 이터레이터의 역할과 for … of 문(과 스프레드 문법, 배열 디스트럭처링)에 넣을 수 있는 이터러블의 역할을 동시에 할 수 있다.

  • 이터러블이면서 이터레이터인 피보나치 함수 예시:
    // 이터러블이면서 이터레이터인 객체를 반환하는 함수
    const fibonacciFunc = function (max) {
    	let [pre, cur] = [0, 1];
    
    	return {
    		// Symbol.iterator 메소드는 this를 반환도록 한다. 
    		[Symbol.iterator]() { return this; },
    
    		// next 메소드는 똑같이 이터레이터 리전트 객체를 반환하도록 한다. 
    		next() {
    			[pre, cur] = [cur, pre + cur];
    			return { value: cur, done: cur >= max };
    		}
    	};
    };
    
    let iter = fibonacciFunc(10);
    console.log(iter.next()); // { value: 1, done: false }
    console.log([...iter]);   // [ 2, 3, 5, 8 ]

이터러블이나 이터레이터를 만드는 입장에서,

next에는 언제 순회를 멈출 것인지의 조건을 표현하고,

Symbol.iterator() { return this }를 넣음으로 이터러블의 이득도 전부 챙기게 만들 수 있다.

next 메소드가 반환하는 객체(이터레이터 리절트 객체)에 done 프로퍼티를 제거하면 무한 이터러블을 만들 수 있다.

  • 이터러블이면서 이터레이터인 무한 피보나치 함수 예시:
    // 무한 이터러블을 생성하는 함수
    const fibonacciFunc = function (max) {
    	let [pre, cur] = [0, 1];
    
    	return {
    		[Symbol.iterator]() { return this; },
    
    		next() {
    			[pre, cur] = [cur, pre + cur];
    			return { value: cur, }; // done이 없는 것이 핵심. 
    		}
    	};
    };
    
    // Max 값을 전달하지 않고 무한 이터러블을 생성한다. 
    for (const num of fibonacciFunc()) {
    	if (num > 10000) break;
    	console.log(num);
    }

    ⇒ ’무한’을 조작하기 위해 done 프로퍼티를 조작하기 위해 next 메소드를 조작하기 위해 객체는 이터레이터여야 하고, 또 for … of 문 등으로 편하게 사용하기 위해 Symbol.iterator()를 가지는 이터러블로 구현한다.

지연 평가 (Lazy evaluation)

: 평가 결과가 필요할 때까지 평가를 늦추는(=값을 생성하지 않는) 기법.

: 데이터가 필요한 시점 이전까지는 미리 데이터를 생성하지 않다가 데이터가 필요한 시점이 되면 그때야 비로소 데이터를 생성하는 기법.

모든 데이터를 메모리에 미리 확보한 다음 데이터를 공급하는 배열이나 문자열 등과 달리,

  1. 불필요한 메모리를 소비하지 않는다.
  1. 실행 속도가 빠르다.
  1. 무한도 표현할 수 있다.

는 장점이 있다.

‘데이터가 필요한 시점’ = next 메소드가 호출될 때 (=for … of 문이나 배열 디스트럭처링 할당 등이 실행될 때)

마무리: 이터레이션 프로토콜

이터레이’션’ 프로토콜은 이터러블 프로토콜과 이터레이터 프로토콜을 말한다.

아래의 데이터 소스는 모두 이터레이션 프로토콜을 준수하는 이터러블이다.

Array , String , Map , Set , TypedArray (Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array), DOM 컬렉션(NodeList, HTMLCollection), arguments

ES6에서는 순회 가능한 데이터 컬렉션을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 for … of 문, 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용할 수 있도록 일원화했다.

(참고: 모던 자바스크립트 Deep Dive - 이웅모, 위키북스)


※ 위 내용은 스스로 공부하며 적어둔 노트이며 불확실한 내용이 있을 수 있습니다. 학습용으로 적합하지 않음을 유념해주세요. ※


Uploaded by N2T

    'TIL | WIL' 카테고리의 다른 글
    • 4/25 화 [프로그래머스] 해시(Hash), 완주하지 못한 선수와 Object, Map의 시간복잡도 TIL
    • 4/24 월 (클라우드 컴퓨팅의 이점과 분류) TIL
    • 4/21 금 (모듈과 테스트 코드에 대하여) TIL
    • 4/20 목 (프로미스(async/await)와 클로저) TIL
    깊은바다거북
    깊은바다거북

    티스토리툴바