[프로그래머스] 실패율
2021.10.25에 푼 문제입니다.
이번에도 역시나... 잘 안 풀려서ㅠ 남친님한테 SOS해서 풀었습니다.😂
남친님이 치는 코드 보면 존경스러워요ㅠㅠ 너무 잘해요.
그냥 봐도 이해가 되고 자바스크립트 치는 사람도 아닌데 왜 이렇게 잘 푸는지...🤣
저도 풀다보면 잘 풀겠죠.
- N : 전체 스테이지의 개수
- stages : 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열
- 실패율 : 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수
실패율 문제를 풀기 위해서 실패율에 대한 이해가 필요해요.
솔직히 실패율은 글로 읽을 때 이해가 안돼서 입출력 설명 보면서 이해했습니다.
실패율을 쉽게 설명하면 실패율 = 앞 / 뒤 라고 하면
- 앞 : stage배열 안에 있는 n번의 개수
- 뒤 : stage에 전체 원소 - 이전 번의 stage 원소 개수
테스트 1로 설명을 하자면
N : 5 -> 1, 2, 3, 4, 5 스테이지가 있습니다.
stages = [2, 1, 2, 6, 2, 4, 3, 3]
- 1 : 1개
- 2 : 3개
- 3 : 2개
- 4 : 1개
- 5 : 0개
- 6 : 1개
앞은 이렇게 됩니다.
그러나 제한사항에 "스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0으로 정의한다." 했음으로 스테이지 6은 생략하겠습니다.
여기서 뒤는 누적이 됩니다.
- 1번 : 1보다 작은 스테이지는 없음으로 현재 stage 배열 길이인 8입니다.
- 2번 : 이전 뒤의 수 8 임으로 8 - 1번의 stage 원소 개수(1개) = 8 - 1 = 7
- 3번 : 이전 뒤의 수 7 임으로 7 - 2번의 stage 원소 개수(3개) = 7 - 3 = 4
- 4번 : 이전 뒤의 수 4 임으로 4 - 3번의 stage 원소 개수(2개) = 4 - 2 = 2
- 5번 : 이전 뒤의 수 2 임으로 2 - 4번의 stage 원소 개수(1개) = 2 - 1 = 1
이런 식으로 누적이 됩니다.
그래서 각 스테이지의 실패율은
- 1번 스테이지 실패율 : 1/8 = 0.125
- 2번 스테이지 실패율 : 3/7 = 0.42857142857142855
- 3번 스테이지 실패율 : 2/4 = 0.5
- 4번 스테이지 실패율 : 1/2 = 0.5
- 5번 스테이지 실패율 : 0/1 = 0
이 됩니다.
각 스테이지의 번호를 실패율의 내림차순으로 정렬하면 [3,4,2,1,5] 됩니다.
여기서 제한사항에서 "만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다."라는 항목이 있기 때문에 3번 4번 스테이지의 실패율이 같아도 순서가 3번 먼저 오게 됩니다.
실패율에 대한 설명은 여기까지고요 다음 파트에서는 해결방안에 대해 설명하겠습니다.
해결방안
function solution(N, stages) {
let fRArr = [];
let sSn = stages.length;
// 실패율 구해서 배열에 추가
for(let i=1; i<=N; i++){
let nC = stages.filter(stage => stage == i).length;
fRArr.push(nC / sSn);
sSn -= nC;
}
// 배열을 객체로 변경
const fRMap = fRArr.map((element, index)=>{
return{stage: index+1, value: element};
});
// 정렬
fRMap.sort((a, b)=>{
// vlaue는 내림차순
if (a.value > b.value) return -1;
if (a.value < b.value) return 1;
return 0;
});
// 정렬 완료한 객체를 배열로 변경
let answer = fRMap.map(n =>{
return n.stage;
});
return answer;
}
해결방안 순서
- 실패율 구해서 fRArr배열에 추가하기
- fRArr배열을 객체로 변경하기(index(key), value 설정하기)
- fRMap객체를 value를 기준으로 내림차순으로 정렬
- 정렬완료한 fRMap 객체를 배열로 변경
1단계. 실패율 구해서 fRArr배열에 추가하기
- let fRArr = []; : 실패율 담을 배열
- let sSn = stages.length; : 스테이지 길이
실패율을 구하기 위해서는 stages배열에 해당하는 스테이지가 몇 개인지 알아야 됩니다.
그래서 저는 for문과 filter을 사용해 주었는데요.
MDN Web Docs
filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다.
구문 : arr.filter(callback(element[, index[, array]])[, thisArg])
- callback :각 요소를 시험할 함수. true를 반환하면 요소를 유지하고, false를 반환하면 버립니다. 다음 세 가지 매개변수를 받습니다.
- element : 처리할 현재 요소.
- index Optional : 처리할 현재 요소의 인덱스.
- array Optional : filter를 호출한 배열.
- thisArg Optional : callback을 실행할 때 this로 사용하는 값.
MDN에서는 이렇게 설명되어 있는데
제가 알고 있는걸로 설명하자면
filter는 주로 특정 조건을 만족하는 새로운 배열을 필요로 할 때 사용하는 메서드입니다.
구문 : 배열.filter((처리할 현재 요소) => 필터 할 조건);
// filter()메서드
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result); // Array ["exuberant", "destruction", "present"]
// 1단계.실패율 구해서 fRArr배열에 추가하기
for(let i=1; i<=N; i++){
let nC = stages.filter(stage => stage == i).length;
fRArr.push(nC / sSn);
sSn -= nC;
}
- for(let i=1; i<=N; i++) : 테스트 1에서는 5 임으로 1, 2, 3, 4, 5
- nC변수 : stage배열 안에 있는 n번의 개수(실패율 앞쪽)
stages.filter(stage => stage == i).length;
이 코드는 실패율 앞쪽(stage배열 안에 있는 n번의 개수)을 구하는 코드인데요.
테스트 1을 예시로 설명하자면 (N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];)
i=1일 때
let nC = stages.filter(stage => stage == 1).length;
여기서 fliter부분의 조건을 보면
- "stage == 1" -> stage배열에서 원소 1인 것만 추출해라 -> [1]
let nC = [1].length -> 1
let nC = 1
i=2일 때
let nC = stages.filter(stage => stage == 2).length;
- "stage == 2" -> [2, 2, 2]
let nC = [2, 2, 2].length -> 3
let nC = 3
i=3일 때
let nC = stages.filter(stage => stage == 3).length;
- "stage == 3" -> [3, 3]
let nC = [3, 3].length -> 2
let nC = 2
i=4일 때
let nC = stages.filter(stage => stage == 4).length;
- "stage == 4" -> [4]
let nC = [4].length -> 1
let nC = 1
i=5일 때
let nC = stages.filter(stage => stage == 5).length;
- "stage == 5" -> []
let nC = [].length -> 0
let nC = 0
여기까지 실패율의 앞쪽(stage배열 안에 있는 n번의 개수)을 구했습니다.
fRArr.push(nC / sSn);
sSn -= nC;
요 코드는 실패율 구하고 fRArr 배열에 담아주는 코드입니다.
- nC / sSn : 실패율
- sSn -= nC; 실패율 뒤(stage에 전체 원소 - 이전 번의 stage 원소 개수) 차감되는 걸 구함
앞에 실패율 설명에서 실패율 = 앞 / 뒤라고 하면
- 앞 : stage배열 안에 있는 n번의 개수
- 뒤 : stage에 전체 원소 - 이전 번의 stage 원소 개수
라고 했는데요.
여기서 중요한 점이 뒷부분은 "stage에 전체 원소 - 이전 번의 stage 원소 개수"라는 점입니다.
즉 차감이 된다는 거죠.
그래서 테스트 1을 예시로 설명하자면 (N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];)
스테이지 1(i = 1) 일 때는 실패율 뒷부분이
i=1일 때
let nC = 1
sSn = stages.length; = 8
fRArr.push(nC / sSn); -> fRArr.push(1 / 8);
sSn -= nC; -> sSn - nC = 8 - 1 = 7
i=2일 때
let nC = 3
sSn = 7
fRArr.push(nC / sSn); -> fRArr.push(3 / 7);
sSn -= nC; -> sSn - nC = 7 - 3 = 4
i=3일 때
let nC = 2
sSn = 4
fRArr.push(nC / sSn); -> fRArr.push(2 / 4);
sSn -= nC; -> sSn - nC = 4 - 2 = 2
i=4일 때
let nC = 1
sSn = 2
fRArr.push(nC / sSn); -> fRArr.push(1 / 2);
sSn -= nC; -> sSn - nC = 2 - 1 = 1
i=5일 때
let nC = 0
sSn = stages.length; = 1
fRArr.push(nC / sSn); -> fRArr.push(0 / 1);
sSn -= nC; -> sSn - nC = 1 - 0 = 1
이렇게 진행이 됩니다.
테스트 1을 예시로 설명
N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];
i=1일 때
fRArr =[0.125] = [1/8]
i=2일 때
fRArr =[0.125, 0.42857142857142855] = [1/8, 3/7]
i=3일 때
fRArr =[0.125, 0.42857142857142855, 0.5] = [1/8, 3/7, 2/4]
i=4일 때
fRArr =[0.125, 0.42857142857142855, 0.5, 0.5] = [1/8, 3/7, 2/4, 1/2]
i=5일 때
fRArr = [0.125, 0.42857142857142855, 0.5, 0.5, 0] = [1/8, 3/7, 2/4, 1/2, 0/1]
2단계. fRArr배열을 객체로 변경하기(index(key), value 설정하기)
fRArr 배열에서 스테이지 1부터 차례대로 실패율을 넣어주었습니다.
이 점을 활용해 fRArr배열을 객체로 만들어보려고 합니다.
배열을 객체로 만드는 이유는 결과 값이 실패율 기준으로 실패율이 높은 스테이지부터 내림차순으로 정렬해야 되는데
그러려면 배열을 객체로 만들어 스테이지와 실패율을 같이 제어하는 게 편하기 때문에 만들었습니다.
배열을 객체로 만들기 위해 가장 쉬운 방법은 map을 사용하는 겁니다.
MDN Web Docs
map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.
구문 : arr.map(callback(currentValue[, index[, array]])[, thisArg])
- callback : 새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가집니다.
- currentValue : 처리할 현재 요소.
- index Optional : 처리할 현재 요소의 인덱스.
- array Optional : map()을 호출한 배열.
- thisArg Optional : callback을 실행할 때 this로 사용되는 값.
//map()메서드 예제
const array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x * 2);
console.log(map1); // Array [2, 8, 18, 32]
제 생각대로 써보면
구문 : 배열.map((처리할 현재 요소, 처리할 현재 요소의 인덱스, 호출한 배열) => 어떻게 변경할지)
일반적으로는 배열을 변환할 때 자주 써서 처리할 현재 요소의 인덱스, 호출한 배열은 잘 사용 안 하긴 하는데
이번에 배열을 객체를 만들기 위해
처리할 현재 요소(currentValue), 처리할 현재 요소의 인덱스(index)를 사용하겠습니다.
map()에 대한 설명은 요기 있는👇 블로그 참고해서 써봤습니다.~😊
https://7942yongdae.tistory.com/48
Javascript - Array map 사용법
오늘은 Javascript의 Array가 가지고 있는 map의 정의와 사용법 그리고 활용 방법에 대해 이야기해보려고 합니다. 일단 Array가 가진 map 함수가 어떤 함수인 지부터 살펴보도록 하죠. map 정의 Array.prototy
7942yongdae.tistory.com
// 2단계. fRArr배열을 객체로 변경하기(index(key), value 설정하기)
const fRMap = fRArr.map((element, index)=>{
return{stage: index+1, value: element};
});
여기서 객체를 어떻게 만들고 싶나면,
fRMap = [
{stage : 스테이지1, value : 스테이지1의 실패율}, .... {stage : 스테이지N, value : 스테이지N의 실패율}
]
을 만들려고 합니다.
- element : 처리할 현재 요소
-> 앞에서 실패율을 담은 fRArr의 원소
-> 테스트 1로 설명) fRArr=[0.125, 0.42857142857142855, 0.5, 0.5, 0] - index : 처리할 현재 요소의 인덱스
-> 앞에서 담은 실패율을 담은 fRArr배열의 인덱스
-> 테스트 1로 설명) fRArr배열에 5개가 담겨 있으므로 0~4을 의미 - stage: index+1 : 인덱스는 0부터 시작하는데 스테이지는 1부터 시작함으로 index+1을 해주었습니다.
테스트 1을 예시로 설명
N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];
fRMap = [
{ stage : 1, value: 0.125 },
{ stage : 2, value: 0.42857142857142855 },
{ stage : 3, value: 0.5 },
{ stage : 4, value: 0.5 },
{ stage : 5, value: 0 }
]
3단계. fRMap객체를 value를 기준으로 내림차순으로 정렬
내림차순으로 정렬하기 위해서는 sort()메서드를 사용해야 됩니다.
MDN Web Docs
sort() 메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다.
구문 : 배열.sort(정렬 순서를 정의하는 함수)
sort() 메서드는
- sort((a,b)=>a-b); -> 오름차순
- sort((a,b)=>b-a); -> 내림차순
간단하게 이렇게 되지만
이번에 정렬해야 되는 게 단순한 숫자 배열이 아니 객체라서 조금 다르게 sort를 compare 함수의 형식으로 써야 됩니다.
오름차순
// value 기준으로 정렬 오름차순
let items = [{index: 0, value: 10}, {index: 1, value: 8}, {index: 2, value: 15}];
items.sort(function (a, b) {
if (a.value > b.value) return 1;
if (a.value < b.value) return -1;
return 0;
});
console.log(items);
//items = [{ index: 1, value: 8 }, { index: 0, value: 10 }, { index: 2, value: 15 }]
내림차순
// value 기준으로 정렬 내림차순
let items = [{index: 0, value: 10}, {index: 1, value: 8}, {index: 2, value: 15}];
items.sort(function (a, b) {
if (a.value < b.value) return 1;
if (a.value > b.value) return -1;
return 0;
});
console.log(items);
// items = [{ index: 2, value: 15 }, { index: 0, value: 10 }, { index: 1, value: 8 }]
오름차순과 내림차순에 대한 기준 변경은 -1 에서만 일어납니다.
- if (a.value < b.value) return -1; :오름차순
- if (a.value > b.value) return -1; : 내림차순
return 1에서 a, b의 비교를 return -1에서 쓴 a, b의 비교를 반대로 써주시면 됩니다.
- if (a.value > b.value) return 1; :오름차순
- if (a.value < b.value) return 1; : 내림차순
여기서는 꼭 return 1; return -1; return 0;를 다 써야 됩니다. 안 쓰면 오류 떠요.
객체의 해당 속성 중 하나의 값을 기준으로 정렬
// 3단계. fRMap객체를 value를 기준으로 내림차순으로 정렬
fRMap.sort((a, b)=>{
// vlaue는 내림차순
if (a.value > b.value) return -1;
if (a.value < b.value) return 1;
return 0;
});
테스트 1을 예시로 설명
N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];
fRMap = [
{ stage: 3, value: 0.5 },
{ stage: 4, value: 0.5 },
{ stage: 2, value: 0.42857142857142855 },
{ stage: 1, value: 0.125 },
{ stage: 5, value: 0 }
]
4단계. 정렬 완료한 fRMap 객체를 배열로 변경
실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 만들기 위해
내림차순 한 fPMap객체를 stage만 뽑아서 다시 배열로 변경해야 되는데요.
이때 다시 map() 메서드를 사용했습니다.
let answer = fRMap.map(n =>{
return n.stage;
});
return answer;
테스트 1을 예시로 설명
N =5; stages = [2, 1, 2, 6, 2, 4, 3, 3];
fRMap = [
{ stage: 3, value: 0.5 },
{ stage: 4, value: 0.5 },
{ stage: 2, value: 0.42857142857142855 },
{ stage: 1, value: 0.125 },
{ stage: 5, value: 0 }
]
answer = [3, 4, 2, 1, 5]
여기까지 프로그래머스 실패율 해결방안에 대해 설명해보았습니다.
이번 해결방안은 실패율 설명, filter(), map(), sort() 때문에 길어졌는데요.
3개의 메서드 filter(), map(), sort() 에 대해서 잘 아신다면 쉽게 풀 수 있었던 문제인 것 같아요.
저도 이번 기회에 3가지 함수 대해서 조금 더 알게 되어서 뿌듯합니다.😆