JavaScript/알고리즘

프로그래머스 추억 점수 JS ( 객체 프로퍼티 할당하기, 각각 다른 배열 index로 연결하기 )

hihiha2 2023. 7. 16. 01:25

문제 설명

사진들을 보며 추억에 젖어 있던 루는 사진별로 추억 점수를 매길려고 합니다. 사진 속에 나오는 인물의 그리움 점수를 모두 합산한 값이 해당 사진의 추억 점수가 됩니다. 예를 들어 사진 속 인물의 이름이 ["may", "kein", "kain"]이고 각 인물의 그리움 점수가 [5점, 10점, 1점]일 때 해당 사진의 추억 점수는 16(5 + 10 + 1)점이 됩니다. 다른 사진 속 인물의 이름이 ["kali", "mari", "don", "tony"]이고 ["kali", "mari", "don"]의 그리움 점수가 각각 [11점, 1점, 55점]]이고, "tony"는 그리움 점수가 없을 때, 이 사진의 추억 점수는 3명의 그리움 점수를 합한 67(11 + 1 + 55)점입니다.

그리워하는 사람의 이름을 담은 문자열 배열 name, 각 사람별 그리움 점수를 담은 정수 배열 yearning, 각 사진에 찍힌 인물의 이름을 담은 이차원 문자열 배열 photo가 매개변수로 주어질 때, 사진들의 추억 점수를 photo에 주어진 순서대로 배열에 담아 return하는 solution 함수를 완성해주세요.

 

🙋‍♀️ 내 생각

photo의 배열을 돌면서 name과 일치하면, 그만큼 yearning의 숫자를 합하여 배열로 만드는 문제이다.

 

이 문제를 풀면서 어려웠던 점은 2가지였다.

1.photo[i]를 한번 더 돌아야겠다는 것까지는 이해했는데, 어떻게하면 돌면서 name과 일치하는지 검사할 것인가?

2.name과 yearning을 어떻게연결할까?

였다.

 

photo의 배열을 돌면서 또 그안에서 한번 더 돌면서 name과 일치하는지를 확인하고 특정한 작업을 하기위해서 중첩for문을 사용했다.

또 name과 yearning의 값을 연결하기 위해서 객체를 만들었다.

 

평소 객체를 만드는 연습을 많이 해보지 않아서, 객체의 key-value값 넣어주기와 일치하는 key값이 있을때 value를 수정하기를 하면서 조금 어려움을 겪었다.

 

객체지향프로그래밍을 위해서라도 객체는 꼭 정확히 이해하고 있어야한다고 생각이 들어서 문제를 풀면서 공부했다.

 

✅ 내 코드

function solution(name, yearning, photo) {
  const nameAndYearning ={};
  const result=[]  
  for(i=0; i<name.length; i++){
      nameAndYearning[name[i]] = yearning[i]
  }  
  for(i=0; i<photo.length; i++){
      let point = 0;
      for(j=0; j<photo[i].length; j++){
           point += nameAndYearning[photo[i][j]] || 0
      } result.push(point);
  } return result 
}

우선, nameAndYearning이라는 이름으로 빈객체를 만들었다.

(name과 해당하는 value값을 연결하기 위해서 객체를 생성한다)

 

다음으로 result를 담을 배열을 선언하고 빈배열로 초기화한다.

 

그런다음 for문을 통해서 nameAndYearging의 객체를 완성한다.

nameAndYearning이라는 이름의 객체에 프로퍼티를 추가하기 위해서 []대괄호 표기법을 사용한다.

 

 

 


🔫 객체에 프로퍼티 할당하는 방법

자바스크립트에서 객체에 값을 추가하는 방법은 크게

1.객체리터럴

2. dot notation(점 표기법)

3.bracket notation(대괄호 표기법)

가 있다.

 

 

1.객체리터럴

const obj = {
  key1: value1,
  key2: value2,
  // ...
};
const person = {
  name: 'John',
  age: 25
};

key-value의 값을 직접 할당한다.

 

 

2. dot notation(점 표기법)

const obj = {};
obj.key1 = value1;
obj.key2 = value2;
const person = {};
person.name = 'John';
person.age = 25;

.을 이용하여 객체의 key값에 접근한 후, value를 할당한다.

 

 

3.bracket notation(대괄호 표기법)

const obj = {};
obj['key1'] = value1;
obj['key2'] = value2;
const person = {};
const key = 'name';
person[key] = 'John';
person['age'] = 25;

 


다시 코드로 돌아오면, nameAndYearning이라는 이름의 객체에 대괄호 표기법을 통해서 프로퍼티를 할당하는 것을 볼 수 있다.

nameAndYearning[name[i]]를 통해서 name[i]라는 key값에 접근하고, 그 해당 key에 yearning[i]라는 value를 할당한다.

 for(i=0; i<name.length; i++){
      nameAndYearning[name[i]] = yearning[i]
  }

객체리터럴과 유사하게 나타내면, 아래와 같을 것이다.

nameAndYearning = {

name[i] = yearning[i]

}

 각각의 인덱스에 맞는 key-value를 for문을 돌면서 객체에 할당한다.

 

nameAndYearning라는 객체를 만들었다.

여기까지 만들고 nameAndYearning을 테스트코드에 넣어서 확인해보면 아래와 같다.

name과 yearning의 값들이 각각 key와 value로 연결되어 nameAndYearning라는 객체를 이루고 있음을 확인할 수 있다.

 

다음으로 각각의 점수를 계산하기 위해서 photo의 길이를 갖는 for문을 만든다. 

photo배열은 배열안에 배열이 있는 구조이므로 배열안의 배열의 각 요소를 돌기위해서 중첩for문을 사용한다.

for(i=0; i<photo.length; i++){
      let point = 0;
      for(j=0; j<photo[i].length; j++){
           point += nameAndYearning[photo[i][j]] || 0
      } result.push(point);
  }

for문의 안에서는 point라는 변수를 선언하고 0으로 값을 초기화한다.

 

안쪽의 for문은 photo[i]를 길이로 갖는다. 

그런다음 배열을 순회하면서 point라는 변수에 포인트를 계산하도록 한다.

 

nameAndYearning과 key값이 일치할 경우, pointnameAndYearning의 value를 더해야한다.

nameAndYearning[photo[i][j]]를 통해서 nameAndYearning의 photo[i][j]라는 key값을 찾는다.

만약 일치하는 key값이 있으면 그 key값에 담긴 value를 point에 더하고, 일치하는 값이 없을 경우에는 0을 더한다.

 

여기까지 하고 console.log를 통해서 값을 확인하면 아래와 같이 나온다.

point에 각 key에 해당되는 value가 들어가는 것을 확인할 수 있다.

 

각각의 point가 담긴 배열을 최종값으로 리턴해야하기때문에 push를 통해서 result라는 배열에 담는다.  result.push(point)

for(i=0; i<photo.length; i++){
      let point = 0;
      for(j=0; j<photo[i].length; j++){
           point += nameAndYearning[photo[i][j]] || 0
      } result.push(point);
  }

이렇게 하면 pass!!

 

 

 

 

💻  다른사람 코드중에 배울 것 1

function solution(name, yearning, photo) {
    let obj = {};
    for(let i = 0; i < name.length; i++){
        obj[name[i]] = yearning[i];
    }
    return photo.map(value => value.map(v => obj[v] ? obj[v] : 0).reduce((acc,cur) => acc + cur,0))
}

객체를 이용해서 풀고 기본적인 원리는 거의 비숫하지만, 중첩for문 대신 map을 사용하고 push대신 reduce를 사용한 코드가 있어서 공부해보았다.

 

우선 obj라는 이름의 빈객체를 만들고 초기화한다.

그런다음 for문을 순회하면서 obj라는 객체에 name[i]라는 key값에 yearning[i]value로 할당한다.

(위의 코드와 여기까지는 동일하다)

 

photo를 map을 돌면서 value => value.map()을 통해서 map안에서 다시 map을 돌린다

(중첩for문을 돌리는것과 유사한 원리라고 생각하면 될 것같다)

 

안쪽의 map에서는 obj[v]? obj[v] : 0를 사용하여 만약 obj객체에서 v라는 key값에 해당하는 값을 가져온다.

obj[i]라는 key값이 있으면 그에 해당하는 value를, 없으면 0 반환한다.

 

이렇게해서 나오는 숫자값들을 합치기 위해서 reduce를 사용한다.

(위의 코드에서는 point += 를 사용한것과 유사하고 생각하면 된다)

 

여기서 헷갈리는 지점이 있어서 아래와 같이 코드를 조금 부분적으로 나누어 생각해보았다.

return photo.map(value => value.map(v => obj[v] ? obj[v] : 0).reduce((acc,cur) => acc + cur,0))

map은 보통 배열을 반환하기 때문에 map안에서 map을 돌려서 배열안의 배열값이 나오지는 않을까?라는 생각이 들었다.

하지만 reduce를 사용하여 안쪽 map의 합산만을 구하여 바깥쪽 map에 보내기때문에 배열이 reduce를 통해서 평탄화되는 효과를 얻게 된것을 알 수 있다.

 

reduce코드만 삭제하고 테스트를 통해서 보면 조금 더 정확하게 알 수 있다.

아래와 같이 배열안에 배열이 담긴 값이 나온다.

reduce를 통해서 이런 구조를 가지고 있던 코드가, 

[
  [result1, result2, result3, result4],
  [result5, result6, result7, result8],
  [result9, result10, result11, result12],
]

이런 구조로 바뀐 것이다.

[sum1, sum2, sum3]

 

그렇게 구한 합산의 값이 바깥쪽 map을 통해서 배열로 반환되면 끝!

 

 

 

 

 

💻  다른사람 코드중에 배울 것 2

function solution(name, yearning, photo) {
    return photo.map((v)=> v.reduce((a, c)=> a += yearning[name.indexOf(c)] ?? 0, 0))
}

이 코드는 위의 2개의 코드와는 다르게 따로 객체를 만들어서 처리하지 않았고, 배열의 인덱스의 값을 이용하여 풀었다.

따로 객체를 만들지 않았기때문에 코드가 훨씬 간결하다.

 

우선 map을 통해서 photo배열을 순회한다. 

 

photo의 각 요소역시 배열이기때문에 reduce를 통해서 각 요소의 값을 더할 수 있다.

v.reduce를 통해서 photo안에 들어있는 배열안에 있는 값들을 더하는 코드를 짠다.

 reduce((a,c) => a += yearning[name.indexOf(c)]) 를 통해서 photo안의 배열안의 각 요소의 name의 index의 찾고 그 인덱스의 값을 yearning에 넣는다. 그러면 yearning에서 해당하는 값을 불러올 것이다. 그 값을 a더해줌으로써 값을 축적한다. 

v 배열의 각 요소 c를 순회하면서 누적값 a를 갱신한다.

 

??은 nullish 병합 연산자(nullish coalescing operator)이다.

이 연산자는 왼쪽 피연산자가 null 또는 undefined인 경우, 오른쪽 피연산자를 반환합니다. 그렇지 않은 경우에는 왼쪽 피연산자를 반환한다. ??연산자를 통해서 값이 null도는 undefined이 될 경우를 대비한다.

 

이렇게 더한 값은 v가 되어 map을 통해서 []배열로 들어가서 반환된다.

 

 

 

🙋‍♀️ 내 생각

객체지향프로그래밍을 만들기위해서는 객체를 이해하는 것이 필수적이다.

공부하다보면 객체를 많이 접하지만 막상 객체를 만들고 수정하고 처리하는 것들은 아직 많이 해보지 않아서 부족한 것같다.

이 문제를 풀면서 배열을 순회하면서 각각의 값을 객체로 만드는 과정을 공부하였다.

굳이 객체를 이용해야만 풀 수 있는 문제는 아니었지만, 이런 방식을 통해서 풀어봄으로써 객체를 더 공부하고 이해할 수 있어서 좋았다.

 

맨 마지막 코드처럼 배열의 인덱스를 이용해서 2개의 배열을 연결하고 reduce를 통해서 point를 합산하는 방법은 생각지도 못했던 방법이라 새로웠다. 코드가 짧지만 가독성도 좋아서 효율적인 것 같다고 생각했다. 앞으로 이런식으로 짜는 방법도 생각해봐야겠다.