프로그래머스 최빈값 구하기 문제에서 트러블이 발생했다.
문제 설명
최빈값은 주어진 값 중에서 가장 자주 나오는 값을 의미합니다. 정수 배열 array가 매개변수로 주어질 때, 최빈값을 return 하도록 solution 함수를 완성해보세요. 최빈값이 여러 개면 -1을 return 합니다.
제한사항
0 < array의 길이 < 100
0 ≤ array의 원소 < 1000
[1, 2, 3, 3, 3, 4]에서 1은 1개 2는 1개 3은 3개 4는 1개로 최빈값은 3입니다.
[1, 1, 2, 2]에서 1은 2개 2는 2개로 최빈값이 1, 2입니다. 최빈값이 여러 개이므로 -1을 return 합니다.
[1]에는 1만 있으므로 최빈값은 1입니다.
빈 객체를 만들고 for 문으로 처음 만나게 되는 숫자는 key와 value 값을 넣어준 뒤,
기존에 있던 숫자는 해당되는 Key값을 찾아서 숫자를 1씩 카운트해주게 만들었다.
그러나 출력값에는 { '0': 1, '1': 1, '2': 1, '3': 1, '4': 1, '5': 1 }
가 출력되었다.
주어지는 예시에서 array의 0은 들어있지 않은데 값이 들어갔다.
for in 문을 써서 배열의 index 값이 그대로 들어간 듯 하다.
이후 for of문으로 바꿨더니 정상 출력이 되었다.
{ '1': 1, '2': 1, '3': 3, '4': 1 }
이 문제로 인해 for in 과 for of의 차이점이 확실히 각인되었다.
for in
문은 요소로 배열의 index 값이 들어간다.(객체일 경우 key값이 들어간다.)
for of
문은 배열 요소 자체가 들어간다. 즉 배열 내에 있는 값들이 들어갈 수 있다는 것이다.
이제 조건에 맞게 가장 큰 값을 찾아야하는데 완전탐색으로 찾는 방법이 있긴 하지만 시간복잡도를 너무 잡아먹을 것 같아서 내림차순 정렬을 해야할 것 같다.
배열을 정렬해본 적은 있지만, 어떻게 객체를 value
를 기준으로 정렬해야 하는지 몰라 찾아보았다.
const obj = {
i: 5,
c: 3,
b: 9,
m: 0,
};
// value로 오름차순 정렬
const out3 = Object.fromEntries(
Object.entries(obj).sort(([,a],[,b]) => a < b? -1: 1 )
);
console.log(out3) // { m: 0, c: 3, i: 5, b: 9 }
// value로 내림차순 정렬
const out4 = Object.fromEntries(
Object.entries(obj).sort(([,a],[,b]) => a > b? -1: 1 )
);
console.log(out4) // { b: 9, i: 5, c: 3, m: 0 }
구글링을 통해 얻은 정보로는 위와 같이 적용하면 내가 원하는 Value값으로 내림차순 정렬을 할 수 있다.
Object.fromEntries()
는 객체를 배열로 변환,
Object.entries()
는 배열을 객체로 변환해준다.
결론적으로, 배열로 만들어서 정렬한 뒤에 다시 객체로 만들어 주는 것이다.
// 적용 후 코드
function solution(array) {
const check = {};
for(const i of array){
if(!(i in check)){
check[i] = 1;
}else {
check[i] ++;
}
}
console.log(check)
const sorted = Object.fromEntries(
Object.entries(check).sort(([,a],[,b]) => a > b? -1: 1 )
);
console.log(sorted)
if (sorted.length > 1){
}
}
하지만 결과는 그대로....
이후 구글링한 코드에 key값을 문자가 아닌 숫자로 바꾸어보니 정렬되지 않는다를 알게 되었다.
여기서 Object.fromEntries()
을 제외하고 배열의 상태에서 출력해보니 정상적으로 sort
된 것을 알 수 있었다.
const obj = {
'1': 5,
'2': 3,
'3': 9,
'4': 0,
};
// value로 내림차순 정렬
const sorted = Object.entries(obj).sort(([,a],[,b]) => a > b? -1: 1 )
console.log(sorted)
여기까지 배열에서 객체 변환이 문제라는 것을 확인했다.
왜 숫자만 정렬이 돌아가는 것일까 알아보니 다음과 같은 특징이 있었다.
객체는 '특별한 방식'으로 정렬된다.
즉, key값이 정수이기 때문에 배열로 바꿔서 Data를 가지고 있는게 맞음. (추가적으로 map을 쓰는게 더 맞다.)
배열인 상태로 코드를 이어 작성했다.
function solution(array) {
let answer = 0;
const check = {};
for(const i of array){
if(!(i in check)){
check[i] = 1;
}else {
check[i] ++;
}
}
const sorted = Object.entries(check).sort(([,a],[,b]) => a > b? -1: 1 );
if (sorted.length > 1){
if(sorted[0][1] !== sorted[1][1]){
answer = Number(sorted[0][0]);
}else{
answer = -1;
}
}
else{
answer = Number(sorted[0][0]);
}
return answer;
}
console로 sort된 sorted
배열 출력 값을 확인 한 뒤 조건문으로 기능을 완성했다.
아래코드는 Map을 활용한 코드이다.
function solution(array) {
let m = new Map();
for (let n of array){
m.set(n, (m.get(n) || 0)+1);
}
m = [...m].sort((a,b)=>b[1]-a[1]);
return m.length === 1 || m[0][1] > m[1][1] ? m[0][0] : -1;
}
해석해보면,
먼저 Map형태를 가지는 m을 선언해준다.
for~of문으로 값을 꺼내어 Map의 set 메서드로 Key에 n값을 넣는다. m.set(key, value)
형식으로 사용함.
value에는 Map의 get 메서드로 변수 m에서 Key
가 n인 value 값을 가져온다.
||
를 사용하여 가져온 value
가 undefined
즉, falsy한 값이 오면 0을 반환하여 value를 1로 세팅한다.
(array에 있던 값이 처음 들어오게 되면 m에는 Key값이 존재하지 않아 undefined를 반환하게 된다.)
* ||
연산자는 첫 번째 truthy를 찾는다. truthy가 하나도 없다면 마지막 피연산자를 반환한다.
이후 m을 spread 연산자로 배열형태로 전개시켜 내림차순 정렬한다.
(각 인덱스 값의 [0]은 key
, [1]은 value
가 들어있다. b[1]-a[1]
을 하게 되면 내림차순 정렬)
m.length === 1
or m[0][1] > m[1][1]
이면 m[0][0]
를 리턴.false
면 -1
리턴.