Pete, the baker <5kyu>

jjanmo·2020년 1월 12일
0

Codewars에서 뒹굴기

목록 보기
16/32

문제링크

문제

1️⃣ Pete likes to bake some cakes. He has some recipes and ingredients. Unfortunately he is not good in maths. Can you help him to find out, how many cakes he could bake considering his recipes?

2️⃣ Write a function cakes(), which takes the recipe (object) and the available ingredients (also an object) and returns the maximum number of cakes Pete can bake (integer). For simplicity there are no units for the amounts (e.g. 1 lb of flour or 200 g of sugar are simply 1 or 200). Ingredients that are not present in the objects, can be considered as 0.

3️⃣ Examples:

// must return 2
cakes({flour: 500, sugar: 200, eggs: 1}, {flour: 1200, sugar: 1200, eggs: 5, milk: 200}); 
// must return 0
cakes({apples: 3, flour: 300, sugar: 150, milk: 100, oil: 100}, {sugar: 500, flour: 2000, milk: 2000}); 

🚩문제해석
레시피(recipe)에 따라 주어진 재료(ingredient)로 만들 수 있는 케이크의 최대 개수를 구하시오. 레시피와 재료는 모두 Object type으로 주어진다. 또한 재료에 대한 단위는 생각하지 말고 숫자로 접근하고 만약에 재료의 이름이 없다면 그 재료는 주어지지 않은 것이라고 본다.( 0으로 간주함)

문제접근과 풀이

1	function cakes(recipe, available) { 
2		let min; 
3		const recipeKey = Object.keys(recipe); 
4		const availableKey = Object.keys(available);
5		recipeKey.forEach(ele => {
6			if(availableKey.includes(ele)){ 
7				let tmp = Math.floor(available[ele] / recipe[ele]); 
8				min = min === undefined ? tmp : (tmp < min ? tmp : min); 
9			} 
10			else min = 0; 
11		}); 
12		return min; 
13	}
  • 첫번째 생각
    레시피에 있는 재료가 주어진 재료에 있는지를 먼저 확인해야했다. 만약에 주어진 재료에 레시피 재료가 1개라도 없다면, 케이크를 한 개도 만들 수 없다. 이 때 사용한 것은 Object.keys() 메소드이다.

    	   Object.keys()
    	   객체의 키값(속성 이름)을 배열로서 반환한다.
    	``` 
    	 Object.key() 메소드는 이 문제를 배열로서 접근할 수 있게 만들어 준다.  그래서 forEach()**(라인 5)**와 includes()**(라인 6)**를 통해서 레시피와 재료를 매칭시켰다. 
    
  • 두번째 생각
    이제 만들 수 있는 케이크의 개수를 구해야 한다. 같은 키값을 지닌 레시피와 재료를 /을 통해서 나온 몫이 만들 수 있는 개수가 된다. 하지만 재료마다 만들수 있는 개수가 다르기 때문에 그 중에서 가장 작은 값이 주어진 재료로 만들수 있는 최대값이 된다. (라인 7,8)

  • 세번째 생각
    forEach()로 순회를 하면서 나온 개수의 값을 어떻게 비교할 것인가? 변수 min 과 변수tmp 을 순회 할 때마다 비교하여 작은 값을 min 담아주었다. 하지만 이 때, 첫번째 순회인 경우엔 비교할 값이 없기 때문에 비교하지 않고 tmp 값을 min에 넣어주어야 한다. 이러한 첫번째 순회에서의 예외(?)를 해결하기 위해서 자바스크립트에서는 변수를 선언만하고 할당하지않으면 초기값으로 undefined 값을 갖게 된다는 것을 이용하였다. (라인 8)

Best Solution

🔽 나와 비슷한 접근이지만 reduce()와 논리연산자를 활용한 더 깔끔한(?) 풀이이다.

1 	function cakes(recipe, available) {
2  	 return Object.keys(recipe).reduce(function(val, ingredient) {
3   	 return Math.min(Math.floor(available[ingredient] / recipe[ingredient] || 0), val)
4  	}, Infinity)  
5 	}
  1. reduce()의 활용
    내가 나름 정의한 reduce()를 사용할 수 있는 환경이란, 배열을 순회하면서 최종적으로 하나의 결과값을 내야하는 경우이다. 그게 누적값인지 아닌지는 사실 중요하지 않다. 하지만 이러한 상황에서도 reduce()를 사용할 수 있다는 것을 보면 reduce()는 나에겐 아직도 생소한 메소드임에 분명하다. 여기선 val 값에 최소값이 지속적으로 할당되어서 최종적으로
  2. Infinity
    Infinity는 말그대로 무한대를 나타내는 값이다. 만약에 Math.min(n, Infinity) 의 결과값은 항상 n이다. 왜냐하면 어떠한 수 <= Infinity 이기 때문이다. 이러한 이유로 최소값을 구할 때의 초기값으로 적합하다고 볼 수 있다. 나의 코드와 비교해보면 나는 첫번째 순회에서의 비교값을 담기 위해서 undefined를 이용했다. 같은 의도를 다르게 표현했다고 보면 될 것 같다.
  3. recipe와 available의 키값(속성) 비교
    나는 키 값 비교를 위해서 recipe와 available 각각의 키 배열을 만들어서 includes()를 이용하여 찾아내었다. 하지만 위 코드에서는 논리연산자를 통해서 골라낸것으로 보인다. 동작원리가 어떻게 되는지 분석해보자. 아래 이미지는 2,3,4라인을 분석한 이미지이다.

🔽 for문을 통한 좀 더 직관적인(?) 풀이

1	function cakes(recipe, available) { 
2		var numCakes = []; 
3		for(var key in recipe){
4			if(recipe.hasOwnProperty(key)){ 
5				if(key in available){ 
6				    numCakes.push(Math.floor(available[key] / recipe[key])); 
7			    }
8			    else{ 
9				    return  0; 
10			    } 
11		    } 
12	    }//for in 
13	    return  Math.min.apply(null, numCakes); 
14	 }
  1. for in : 객체(Object)의 반복
    객체의 속성에 반복해서 접근할 때 for...in 을 사용한다.
for (variable in object) { ... }
object : 접근할 객체
varialble :  접근할 객체의 속성을 변수로 지정한 것
  1. object.hasOwnProperty(key)
    object가 특정 속성을 가지고 있는지를 나타내는 boolean값을 리턴 한다.

  2. in 연산자
    명시된 속성(property)이 명시된 객체(object)에 존재하면 true를 반환하고 그렇지 않으면 false를 반환한다.

property in object 
object : 객체의 이름
property :  속성이름을 나타내는 문자열(그에 대응하는 변수)

✅ hasOwnProperty()와 in연산자의 비교

  • 공통점 : 단순한 POJO(Plain Old JavaScript Object) 스타일을 가지고 특별한 속성을 갖지 않는 객체의 경우에는 두가지 메소드는 같은 역할(객체 내에 속성이 있는지 없는지 여부를 판변하여 boolean 값을 리턴)을 한다.
    - 그렇기때문에 위의 5번 라인의 코드를 보면, if(available.hasOwnProperty(key)) 라고 적어도 무방하다.
  • 차이점 : 상속된 속성을 가진 경우 in 연산자는 그 부분에 있어서 true를 반환하지만, hasOwnProperty()는 false를 반환한다.
  1. Math.min.apply( )
    func.apply(thisArg, argsArray)
    func : 사용할 메소드
    thisArg : func 를 호출하는데 사용될 this 의 값을 나타낸다.(사용될 객체의 값을 나타낸다.)  null 일 경우, 전역 객체로 대체된다.
    arrsArray : func에 전달될 인수로서 유사배열로서 전달된다. 전달되는 인수가 없는 경우, null 또는 undefined 이다. 
> apply() 를 사용하는 이유 : 유용한 메소드들을 재사용이 가능해지고 반복을 피할 수 있다.
 
## 결론
이 문제는 수학적 해석을 어떻게 코드로 구현 할 것인가를 물어보는 문제라 생각한다. 그걸 해결하는 과정에서 object의 속성에 어떻게 접근할 것인가에 대해서 알게 되었다. 또한 reduce()가 사용되는 상황에 대해서 알게 되었다. 다른 사람들의 풀이들을 보면서 접근 방법은 비슷하지만 좀 더 효율적인 코드 혹은 또다른 방법을 배우게 되었다.

## 참고 
[MDN  Object.keys()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
[MDN Infinity](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Infinity)
[MDN for in](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/for...in)
[in 연산자](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/in)
[The Difference Between in and hasOwnProperty in JavaScript](https://masteringjs.io/tutorials/fundamentals/hasownproperty)
[Function.prototype.apply()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)


> 🚀 문제를 풀어나갈 때 생각의 흐름을 정리합니다. 또한 새로운 풀이에 대한 코드를 분석하고 모르는 부분에 대해서 정리합니다. 생각이 다른 부분에 대한 피드백은 언제나 환영합니다. 틀린 내용에 대한 피드백 또한 항상 감사합니다.
profile
눈길을 걸어갈 때 어지럽게 걷지 말기를.

0개의 댓글