[TIL] 객체↔배열 변환 / 객체 정렬 (feat.Map)

hanbyul.choi·2023년 5월 23일
0

[TIL]

목록 보기
7/39

프로그래머스 최빈값 구하기 문제에서 트러블이 발생했다.

문제 설명

최빈값은 주어진 값 중에서 가장 자주 나오는 값을 의미합니다. 정수 배열 array가 매개변수로 주어질 때, 최빈값을 return 하도록 solution 함수를 완성해보세요. 최빈값이 여러 개면 -1을 return 합니다.

제한사항

0 < array의 길이 < 100
0 ≤ array의 원소 < 1000

입출력 예 #1

[1, 2, 3, 3, 3, 4]에서 1은 1개 2는 1개 3은 3개 4는 1개로 최빈값은 3입니다.

입출력 예 #2

[1, 1, 2, 2]에서 1은 2개 2는 2개로 최빈값이 1, 2입니다. 최빈값이 여러 개이므로 -1을 return 합니다.

입출력 예 #3

[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)

여기까지 배열에서 객체 변환이 문제라는 것을 확인했다.

왜 숫자만 정렬이 돌아가는 것일까 알아보니 다음과 같은 특징이 있었다.

객체는 '특별한 방식'으로 정렬된다.

  • 정수 프로퍼티(integer property)는 자동으로 정렬된다.
  • 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬된다.

즉, 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이다.

아래코드는 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;
}

해석해보면,

  1. 먼저 Map형태를 가지는 m을 선언해준다.

  2. for~of문으로 값을 꺼내어 Map의 set 메서드로 Key에 n값을 넣는다. m.set(key, value) 형식으로 사용함.

  3. value에는 Map의 get 메서드로 변수 m에서 Key가 n인 value 값을 가져온다.

  4. ||를 사용하여 가져온 valueundefined 즉, falsy한 값이 오면 0을 반환하여 value를 1로 세팅한다.
    (array에 있던 값이 처음 들어오게 되면 m에는 Key값이 존재하지 않아 undefined를 반환하게 된다.)
    * ||연산자는 첫 번째 truthy를 찾는다. truthy가 하나도 없다면 마지막 피연산자를 반환한다.

  5. 이후 m을 spread 연산자로 배열형태로 전개시켜 내림차순 정렬한다.
    (각 인덱스 값의 [0]은 key, [1]은 value가 들어있다. b[1]-a[1]을 하게 되면 내림차순 정렬)

  1. m.length === 1 or m[0][1] > m[1][1] 이면 m[0][0]를 리턴.
    둘다 false-1 리턴.
    (길이가 1이거나 0번째의 value가 1번째 보다 크면 배열의 0번째 key값을 반환)

0개의 댓글