JS - reduce 메서드 / 논리연산자 / 단축평가 본문
어제 풀던 문제를 다시 돌아본다
문제 내용은 이렇다
'임의의 문자열을 입력받아, 문자열에 각 단어가 몇 번 등장하는지 기록한 객체를 리턴하라.'
* 대소문자는 구분하지 않아야 하며, 단어가 존재하지 않는 경우 빈 객체를 리턴.
* trim 메서드의 사용은 금지.
function wordFinder(str) {
// 여기에 코드를 작성하세요
let sssAtr = str.toLowerCase().split(" ")
console.log(sssAtr)
let sameArr = []
for(let i=0; i<sssAtr.length ; i++){
let a = sssAtr[i]
for(let j = i+1 ; j < sssAtr.length; j++){
let b = sssAtr[j]
console.log("a:", a,"b:", b, i, j)
if(a == b){
sameArr.push(a)
}
}
}
}
처음 해당 문제에는 이중반복문으로 접근했다.
먼저, 대소문자와 단어를 구분하기 위한 문자열 변수를 만들어내고,
이중 반복문을 통해 반복되는 문장이 있을 경우, 빈 배열에 푸쉬해주는 형태였다.
하지만, 위와 같은 방법에는 치명적인 문제가 있었다.
똑같은 단어가 3개 이상 존재하는 경우 중복으로 세어버리는 경우가 존재한다는 것.
더군다나, 중복으로 세어진 단어를 구분하기 위해 또 다른 처리작업이 필요했더랬다.
그러던 중 구글에서 찾은 게, reduce 메서드 / 논리연산자 / 단축 평가와 관련된 개념이었다.
reduce
배열이름.reduce((누산값, 현재요소값, 현재요소의index, 현재배열) => {
return 다음누산값;
}, 초기누산값);
reduce 메서드는 위와 같은 형태를 띄며, forEach / map 과 같은 메서드처럼 배열을 순회한다.
다만, forEach / map 과는 달리 특별한 부분이 있다.
forEach / map 메서드의 경우 단순히 배열 한가지에 하나의 일을 처리한다고 한다면,
reduce 메서드는 과거의 값에 관여를 한다고 볼 수 있다. (때문에, '누산값' 존재)
어려운 말로는 콜백 함수가 동작할 때 return 하는 값이 다음 콜백 함수의 첫 번째 파라미터로 전달한다고 볼 수 있겠다.
예시
const numbers = [0,1,2,3,4]
numbers.reduce((acc ,cur ,i) => {
console.log(`${i}번째 순회`)
console.log(`acc : ${acc} // cur : ${cur}`)
console.log(`acc + cur : ${acc + cur}`)
return acc+cur
}, 0
)
reduce 함수말미에 초기값으로 0으로 설정했기 때문에, 첫 acc 에는 0이 들어오고,
0 번째 순회에는 numbers array의 첫번째 값인 0을 cur로 가져온다.
그리고 첫번째 acc + cur 값으로 받아온 0이 1번째 순회의 acc에 대입된다.
이게 반복되어 마지막에는 numbers array에 모든 값이 합쳐진다.
말 그대로 누산한다는 개념이다.
이 개념을 응용하면 중복된 값을 객체에 넣어줄 수 있다. (우선은 구글에서 떼온 풀이 방법이다)
const result = array.reduce((acc, cur, i) => {
console.log(`${i}번째 순회`);
console.log(`acc : ${acc}`)
console.log(`cur : ${cur}`)
acc[cur] = (acc[cur] || 0) + 1;
console.log('acc[cur]', acc[cur])
return acc;
}, {} );
console.log(result)
주의 깊게 봐야 할 것
- 초기값의 설정은 '중괄호 { }', 즉 빈 객체. 만약 초기값이 '대괄호 []'로 설정되어 있으면 배열에 저장됨.
- 객체의 'key 값'을 동적으로 넣기 위해서는 대괄호를 활용해야 함.
- 그렇다면 연산에 사용된 acc[cur] = (acc[cur] || 0) + 1 은 대체 무엇인가.
논리연산자 (|| , &&), 단축평가
논리연산자는 if문에서 종종 사용하곤 해서, 그 모습이 낯설지는 않다.
1. || (논리합 연산자) / 부제 true 찾기
|| 의 경우, 둘 중 하나만 true면 true 반환
true || false; // true(오른쪽의 false는 볼 것도 없이, 왼쪽의 true 반환)
true || true; // true(오른쪽의 true는 볼 것도 없이, 왼쪽의 true 반환)
만약 왼쪽 피연산자가 문자열이라면, 빈 문자열이 아니기 때문에 true를 반환
"hello" || false; // "hello"
"hello" || true; // "hello"
위와 같은 개념에서 추가적으로 문자열과 문자열이 양쪽 피연산자에 위치한 경우에는
문자열은 true 로 반환되기 때문에 오른쪽 피연산자는 볼 필요가 없음
"hello" || "bye" // "hello" (오른쪽 값은 볼 필요가 없음)
만약 왼쪽 피연산자가 false 라면, 오른쪽 피연산자가 frue / false 여부 결정하게 됨
false || true; // true (오른쪽 값 반환)
false || false; // false (오른쪽 값 반환)
// 오른쪽이 문자열일 경우
false || "hello"; // "hello" (오른쪽 값 반환)
// 앞서 말했다시피 문자열은 true로 반환됨
가벼운 정리 : 왼쪽 값이 true면 바로 반환 / 허나, 왼쪽 값이 false라면 오른쪽 값을 본 뒤 true / false 결정해서 반환
2. &&(논리곱 연산자)
&&의 경우, 그 의미 말마따나 둘다 true여야만 true 반환.
만약 왼쪽 피연산자가 false 인 경우, 오른쪽은 볼 필요도 없이 자연스레 false 반환.
false && true; // false (오른쪽은 상관 없음.)
false && false; // false (오른쪽은 상관 없음.)
false && "hello"; // false (오른쪽은 상관 없음.)
만약, 왼쪽 피연산자에 null이 오는 경우에는, null은 false로 평가됨으로, null을 그대로 반환
null && false; // null
왼쪽 피연산자가 true값이거나 true값으로 평가되는 문자열이 들어가는 경우에는, 오른쪽 값을 반환
true && true; // true
true && false; // false
true && "hello"; // "hello"
"hello" && true; // true
"hello" && false; // false
"hello" && "bye" // "bye"
위와 같은 방식은 '단축 평가'라고 하며, 이는 아래와 같은 조건들에 사용할 수 있다고 한다.
- null / undefined 체크
- 함수 매개변수 기본값 설정
- 조건부 변수값 할당
이제 그러면 acc[cur] = (acc[cur] || 0) + 1 이라는 코드에 조금이나마 접근할 수 있다.
(acc[cur] || 0) 의 식에서 왼쪽 피연산자가 없을 경우(false인 경우)에는 0을 반환하여 acc[cur] = 1 이 되게 됨.
즉, 초기값 객체 {}는 처음 acc로 반환되기 때문에, 해당 객체에 cur값을 key, 1 을 value 값으로 저장되게 되는 것임.
반대로 (acc[cur] || 0) 의 식에서 왼쪽 피연산자가 있을 경우(true인 경우)에는 acc[cur]을 반환, acc[cur] = acc[cur] + 1 이 됨.
즉, acc[cur] += 1 과 같은 형태로, cur 의 value값에 +1 이 추가된다는 의미임.
이를 적용한 예시
예시 문장 : this is the moment this is the day
순회 횟수 | acc | cur | acc[cur] // key : value |
0 | { } | this | { this : 1} |
1 | {this : 1} | is | { this : 1, is : 1} |
2 | {this : 1, is : 1} | the | {this : 1, is : 1, the :1} |
3 | {this : 1, is : 1, the :1} | moment | {this : 1, is : 1, the :1, moment :1} |
4 | {this : 1, is : 1, the :1, moment :1} |
this | {this : 2, is : 1, the :1, moment :1} |
5 | {this : 2, is : 1, the :1, moment :1} |
is | {this : 2, is : 2, the :1, moment :1} |
6 | {this : 2, is : 2, the :1, moment :1} |
the | {this : 2, is : 2, the :2, moment :1} |
7 | {this : 2, is : 2, the :2, moment :1} |
day | {this : 2, is : 2, the :2, moment :1, day :1} |
쉽지 않은 개념이었는데, 이해하고야 말았다.
응용하기까지는 시간이 걸리겠지만, 오늘도 아나스타샤.
'개발 > JavaScript' 카테고리의 다른 글
JS - todoList 복습 // 하위요소 / fetch, then, catch (0) | 2023.01.04 |
---|---|
JS - sort 메서드 / 로또번호 생성하기 (0) | 2023.01.01 |
JS - todolist / 날씨 적용하기(API) (0) | 2022.12.30 |
JS - todoList (0) | 2022.12.29 |
JS - 배열 안 객체 활용하기 (1) | 2022.12.29 |