문제 설명
정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.
🙋♀️ 내 생각
각각 다른 인덱스의 값을 돌아가면서 더해주기 위해서 중첩for문을 사용했다.
자바스크립트에서 중복값제거와 정렬하는 방법을 알고 있다면 이 부분은 쉽게 풀수있는것 같다.
✅ 내 코드
function solution(numbers) {
let result=[];
for(i=0; i<numbers.length; i++){
for(j=i+1; j<numbers.length; j++){
result.push(numbers[i]+numbers[j])
}
}
return [...new Set(result)].sort((a,b)=>a-b)
}
두 개의 값을 더한 값을 더해줄 배열이 필요해서 우선 제일먼저, result를 선언하고 빈배열로 초기화하였다.
그런 다음, 중첩 for문을 사용하여 인덱스의 각자 다른 값을 더하도록 했다.
(예를 들어 인덱스의 값이 [0,1,2,3,4] 라고 하면 0은 자신을 제외하고 1,2,3,4의 인덱스를 가지고 있는 값과 더해야한다.
그런 다음 1은 다시 2,3,4와 더하고, 2는 3,4와 3은 4와 더하고 끝난다. 항상 자신보다 1큰 인덱스를 가진 값과 더하는 것이다)
중첩for문의 바깥부분은 i를 통해서 범위를 설정했다.
그런 다음 안쪽의 for문은 j를 통해서 범위를 정했는데 i+1로 설정하여 i보다 1큰 인덱스로 시작하여 끝까지 돌도록 만들었따.
이렇게 하면 먼저 가장 바깥쪽의 for문이 실행되고 인덱스값을 기준으로 (i=0 + j=1)/ (i=0 + j=2) /(i=0 + j=3) ... 와 같이 반복하고 i=0인 값이 다 끝나고 나면 다시 (i=1 + j=2) / (i=1 + j=3) / (i=1 + j=4)...가 반복되고 다시 i=2가 되고 .. 이렇게 number의 길이만큼 반복할 것이다.
for문이 돌면서 result.push(number[i]+[j]) 을 통해 해당 인덱스의 값을 result라는 배열안에 push한다.
중첩for문의 핵심은 바깥쪽 for문이 시작됐지만, 안쪽의 for문이 다 돌고나서야 다시 바깥쪽의 for문이 돈다는 것이다.
여기까지 진행하고 해당 값을 확인해보면, 아래와 같이 나오는 것을 확인할 수 있다.
배열안의 중복되는 값을 제거하기 위해서 Set객체를 이용한다.
new Set()을 통해서 해당 배열을 Set객체로 만들어서 중복값을 제거할 수 있다.
이렇게 만들면 중복이 제거된 객체가 생성되기 때문에 다시 배열로 만들기 위해서 ...전개연산자를 사용하고 []처리를 해준다.
(중복제거를 위해서 Set객체를이용했을뿐이지, 중복제거를 위한 메서드는 아니다. Set객체가 중복제거를 허용하지 않는다는 특성을 이용한 것일뿐이다. 전에 다른 문제를 풀면서 Set객체에 대해 공부하고 정리하였다)
마지막으로 오름차순으로 바꾸기 위해서 sort()를 이용한다.
sort((a,b)=>a-b)는 오름차순 정렬을 해준다. 만약 반대로 내림차순을 하고 싶으면 b-a를 한다.
✔️ 개선할 점
빈배열을 담을 변수의 이름은 result라고 지었는데, 최종값은 아니기때문에 임시적으로 값을 저장한다는 것을 의미하는 temp로 바꾸는 것이 좋을 것 같다는 생각이 들었다. 성능적인 문제는 아니지만 그래도 명시적으로 어떤 기능을 하는지 나타내는 것이 좋기때문에 다음부터는 그렇게 지어야겠다.
🤔 중첩for문말고 map을 사용해서 풀수는 없을까?
나는 실제 프로젝트를 구현할때는 리액트로 만들기때문에 주로 map을 많이 사용한다. 그래서 이 문제를 알고리즘이고 for문을 이용해서 어렵지 않게 풀 수 있지만, 그래도 map을 이용해서 풀 수 있다면 그렇게 푸는 방법도 알면 좋을것같다는 생각이 들었다.
그래서 같은 기능을 하는 코드는 map으로 바꾸는 방법도 공부해보았다.
function solution(numbers) {
const temp = numbers.flatMap((value, i) => {
return numbers.slice(i + 1).map((otherValue) => value + otherValue);
});
return Array.from(new Set(temp)).sort((a, b) => a - b);
}
중첩for문대신에 map안에 map을 사용함으로써, 중첩for문과 같은 기능을 수행한다.
우선, map은 새로운 배열을 생성하기때문에 그 값을 담아줄 temp라는 변수를 선언한다. const temp =
flatMap을 통해서 numbers를 순회한다.
여기서 map이 아니라, flatMap을 사용하는 이유는 안쪽의 map을 통해서 반환되는 값이 배열이기때문이다.
(map의 반환값은 배열인데, 이것이 반복되기때문에 중첩된 배열이 생성된다. [[ ㅁㅁ],[ㅁㅁ],[ㅁㅁ]] 과 같은 모양이 나올것이다)
그래서 flatMap을 통해서 순회하면 여러개의 배열로 이루어진 반환값들을 평탄화한 값으로 만들면서 반복할 수 있다.
만약 바깥쪽 map을 flatMap이 아니라 map을 사용하면 아래와 같은 결과가 나온다.
💻 학습한 것
flatMap(): 매핑함수를 사용해 각 엘리먼트에 대해 map 수행 후, 결과를 새로운 배열로 평탄화
let arr1 = [1, 2, 3, 4];
arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
// 한 레벨만 평탄화됨
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
다시 코드로 돌아오면, 그 안에 다시 map을 돌림으로써 중첩for문과 같은 기능을 하도록 한다.
return numbers.slice(i + 1).map((otherValue) => value + otherValue);
✔️ 주의할점: 내부 map은 return처리를 해주어야 한다.
map은 새로운 배열을 생성하는 기능만을 하기때문에 값을 return처리하지않으면 해당 값은 사용되지 못한다.
따라서 바깥쪽의 flatMap에서 안에서 생성된 배열을 이용하기 위해서는 안쪽의 map을 return해야한다.
(반드시 리턴해야 사용할 수 있는것은 바깥쪽 map도 마찬가지일것이다)
안쪽의 인덱스의 시작지점은 numbers의 인덱스보다 1이 커야한다. 이를 위해서 slice()를 사용한다. slice()에 하나의 인자만을 넣으면 이것은 begin지점을 의미한다. 따라서 numbers.slice(i+1)은 i번째인덱스보다 1이 큰 곳부터 시작된 요소들만을 남긴다. 예를 들면 i=0이면 1,2,3,4..이런식으로 1부터 마지막값까지의 인덱스만을 남길 것이다. 그리고 해당 값을 다시 map을 통해서 순회한다.
이때 바깥쪽 flatMap의 value + 안쪽 map의 OtherValue로 더한다.
만약 i=0이면, 인덱스가 1인값부터 더할 것이다. (j=1인것과 동일)
(i=0+j=1)/(i=0+j=2)/(i=0+j=3)/(i=0+j=4)..와 같이 범위만큼을 반복한다.
이 값은 [ㅇㅇ]의 형식으로 리턴된다. 바깥쪽에의해 여러번 반복된다면 [ㅇㅇ],[ㅇㅇ],[ㅇㅇ]의 형식일 것이고 배열로 반환되는 map의 특성상 [[ㅇㅇ],[ㅇㅇ],[ㅇㅇ]] 으로 반환된다. 그래서 flatMap()을 사용하여 [ㅇㅇ]의 형식으로 저장되는 값들을 [ㅇㅇ,ㅇㅇ,ㅇㅇ]과 같이 저장되도록 평탄화해준다.
set을 사용하여 중복된값을 제거하고 sort로 오름차순정렬을 해주는 것은 같다.
🙋♀️ 내 생각
중복제거나 정렬은 전에 해보기도 하고 사용법도 어렵지 않아서 문제를 보자마자 바로 생각났다.
이 문제를 풀면서 가장 오래 생각했던 것은 각자 다른 인덱스값을 가진 것들을 어떻게 더할것인가였다.
0을 1,2,3과 더하고 1을 2,3과 더하고 2를 3과 더하는 것과 같이 풀어야겠다고 생각은 했는데 해당내용을 코드로 쓰려면 어떤 방법을 사용해야할지를 고민했던것같다. 중첩for이 생각나서 풀었는데, 이 문제를 통해서 중첩for문에 대해 조금 더 이해도가 올라간것같다.
다음에 1차이가 아니라 다른 값을 더하거나 빼는 방식으로도 이용해보고 싶다.
또 평소에 리액트로 작업을 하기때문에 map으로 만드는 코드도 공부해보았다. 문제를 풀면서 실질적인 코드를 짤때 어떤 것을 얻을 수 있을까도 생각해보고자 한다. map은 새로운 배열을 생성하는 특징이 있기때문에 리액트의 JSX문안에서 반복된 처리를 할때 많이 사용한다(forEach는 리턴하지 못하기때문에). 또 리액트는 불변성을 지켜야하기때문에도 많이 사용한다. 그래서 반복문을 for문을 통해서 풀 수 있는 경우더라도 map을 통한 방법도 생각해보는 것도 좋을 것 같다.
이 문제를 통해서 map안에서 map을 사용하는 것과 flatMap을 통해서 배열을 평탄화해주는 것을 알게 되었다. 다음에 다른 문제를 통해서 다시 써보고 싶다.
'JavaScript > 알고리즘' 카테고리의 다른 글
프로그래머스 369게임 JS ( 반복문 사용을 위해 number를 이터러블한 타입으로 변환하기 ) (0) | 2023.07.06 |
---|---|
프로그래머스 커피 심부름 JS ( switch로 case나누기 / 삼항연산자로 case제외하기 ) (0) | 2023.07.04 |
프로그래머스 qr code JS ( for문, map() / filter() ) (0) | 2023.07.01 |
프로그래머스 중복된 문자 제거 JS ( set객체, new Set() ) (0) | 2023.07.01 |
프로그래머스 숫자찾기 JS ( indexOf() / || 연산자 ) (0) | 2023.07.01 |